author | Matt Woodrow <mwoodrow@mozilla.com> |
Thu, 17 Apr 2014 17:29:52 +1200 | |
changeset 179104 | 6e72c7a1d9d930fe27ee1c327ef2cf46be6a2f79 |
parent 179103 | 54ac1366823a96f3a8aaed009f1ca570e7c4464b |
child 179105 | ecb6a42d32ff235ca45d202f99a1211a780fee51 |
push id | 26607 |
push user | ryanvm@gmail.com |
push date | Fri, 18 Apr 2014 02:31:26 +0000 |
treeherder | mozilla-central@7fe3ee0cf8be [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | roc |
bugs | 997014 |
milestone | 31.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/content/canvas/src/WebGLContext.h +++ b/content/canvas/src/WebGLContext.h @@ -1029,17 +1029,17 @@ protected: nsLayoutUtils::SurfaceFromElementResult SurfaceFromElement(ElementType* aElement) { MOZ_ASSERT(aElement); uint32_t flags = nsLayoutUtils::SFE_WANT_IMAGE_SURFACE; if (mPixelStoreColorspaceConversion == LOCAL_GL_NONE) flags |= nsLayoutUtils::SFE_NO_COLORSPACE_CONVERSION; if (!mPixelStorePremultiplyAlpha) - flags |= nsLayoutUtils::SFE_NO_PREMULTIPLY_ALPHA; + flags |= nsLayoutUtils::SFE_PREFER_NO_PREMULTIPLY_ALPHA; return nsLayoutUtils::SurfaceFromElement(aElement, flags); } template<class ElementType> nsLayoutUtils::SurfaceFromElementResult SurfaceFromElement(ElementType& aElement) { return SurfaceFromElement(&aElement); }
--- a/content/canvas/src/WebGLContextGL.cpp +++ b/content/canvas/src/WebGLContextGL.cpp @@ -24,16 +24,17 @@ #include "gfxPlatform.h" #include "GLContext.h" #include "nsContentUtils.h" #include "nsError.h" #include "nsLayoutUtils.h" #include "CanvasUtils.h" +#include "gfxUtils.h" #include "jsfriendapi.h" #include "WebGLTexelConversions.h" #include "WebGLValidateStrings.h" #include <algorithm> // needed to check if current OS is lower than 10.7 @@ -2516,16 +2517,20 @@ WebGLContext::SurfaceFromElementResultTo if (!res.mSourceSurface) return NS_OK; RefPtr<DataSourceSurface> data = res.mSourceSurface->GetDataSurface(); if (!data) { // SurfaceFromElement lied! return NS_OK; } + if (!mPixelStorePremultiplyAlpha && res.mIsPremultiplied) { + data = gfxUtils::UnpremultiplyDataSurface(data); + } + // We disallow loading cross-domain images and videos that have not been validated // with CORS as WebGL textures. The reason for doing that is that timing // attacks on WebGL shaders are able to retrieve approximations of the // pixel values in WebGL textures; see bug 655987. // // To prevent a loophole where a Canvas2D would be used as a proxy to load // cross-domain textures, we also disallow loading textures from write-only // Canvas2D's.
--- a/content/html/content/public/HTMLCanvasElement.h +++ b/content/html/content/public/HTMLCanvasElement.h @@ -21,16 +21,19 @@ class nsIDOMFile; class nsITimerCallback; namespace mozilla { namespace layers { class CanvasLayer; class LayerManager; } +namespace gfx { +class SourceSurface; +} namespace dom { class FileCallback; class HTMLCanvasPrintState; class PrintCallback; class HTMLCanvasElement MOZ_FINAL : public nsGenericHTMLElement, @@ -161,16 +164,17 @@ public: /* * nsICanvasElementExternal -- for use outside of content/layout */ NS_IMETHOD_(nsIntSize) GetSizeExternal() MOZ_OVERRIDE; NS_IMETHOD RenderContextsExternal(gfxContext *aContext, GraphicsFilter aFilter, uint32_t aFlags = RenderFlagPremultAlpha) MOZ_OVERRIDE; + virtual TemporaryRef<gfx::SourceSurface> GetSurfaceSnapshot(bool* aPremultAlpha = nullptr); virtual bool ParseAttribute(int32_t aNamespaceID, nsIAtom* aAttribute, const nsAString& aValue, nsAttrValue& aResult) MOZ_OVERRIDE; nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute, int32_t aModType) const MOZ_OVERRIDE; // SetAttr override. C++ is stupid, so have to override both
--- a/content/html/content/src/HTMLCanvasElement.cpp +++ b/content/html/content/src/HTMLCanvasElement.cpp @@ -33,16 +33,17 @@ #include "nsStreamUtils.h" #include "ActiveLayerTracker.h" #ifdef MOZ_WEBGL #include "../canvas/src/WebGL2Context.h" #endif using namespace mozilla::layers; +using namespace mozilla::gfx; NS_IMPL_NS_NEW_HTML_ELEMENT(Canvas) namespace { typedef mozilla::dom::HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement HTMLImageOrCanvasOrVideoElement; @@ -944,10 +945,19 @@ NS_IMETHODIMP HTMLCanvasElement::RenderContextsExternal(gfxContext *aContext, GraphicsFilter aFilter, uint32_t aFlags) { if (!mCurrentContext) return NS_OK; return mCurrentContext->Render(aContext, aFilter, aFlags); } +TemporaryRef<SourceSurface> +HTMLCanvasElement::GetSurfaceSnapshot(bool* aPremultAlpha) +{ + if (!mCurrentContext) + return nullptr; + + return mCurrentContext->GetSurfaceSnapshot(aPremultAlpha); +} + } // namespace dom } // namespace mozilla
--- a/gfx/thebes/gfxUtils.cpp +++ b/gfx/thebes/gfxUtils.cpp @@ -136,16 +136,76 @@ gfxUtils::UnpremultiplyImageSurface(gfxI *dstRow++ = UnpremultiplyValue(a, r); *dstRow++ = UnpremultiplyValue(a, g); *dstRow++ = UnpremultiplyValue(a, b); #endif } } } +TemporaryRef<DataSourceSurface> +gfxUtils::UnpremultiplyDataSurface(DataSourceSurface* aSurface) +{ + // Only premultiply ARGB32 + if (aSurface->GetFormat() != SurfaceFormat::B8G8R8A8) { + return aSurface; + } + + DataSourceSurface::MappedSurface map; + if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) { + return nullptr; + } + + RefPtr<DataSourceSurface> dest = Factory::CreateDataSourceSurfaceWithStride(aSurface->GetSize(), + aSurface->GetFormat(), + map.mStride); + + DataSourceSurface::MappedSurface destMap; + if (!dest->Map(DataSourceSurface::MapType::WRITE, &destMap)) { + aSurface->Unmap(); + return nullptr; + } + + uint8_t *src = map.mData; + uint8_t *dst = destMap.mData; + + for (int32_t i = 0; i < aSurface->GetSize().height; ++i) { + uint8_t *srcRow = src + (i * map.mStride); + uint8_t *dstRow = dst + (i * destMap.mStride); + + for (int32_t j = 0; j < aSurface->GetSize().width; ++j) { +#ifdef IS_LITTLE_ENDIAN + uint8_t b = *srcRow++; + uint8_t g = *srcRow++; + uint8_t r = *srcRow++; + uint8_t a = *srcRow++; + + *dstRow++ = UnpremultiplyValue(a, b); + *dstRow++ = UnpremultiplyValue(a, g); + *dstRow++ = UnpremultiplyValue(a, r); + *dstRow++ = a; +#else + uint8_t a = *srcRow++; + uint8_t r = *srcRow++; + uint8_t g = *srcRow++; + uint8_t b = *srcRow++; + + *dstRow++ = a; + *dstRow++ = UnpremultiplyValue(a, r); + *dstRow++ = UnpremultiplyValue(a, g); + *dstRow++ = UnpremultiplyValue(a, b); +#endif + } + } + + aSurface->Unmap(); + dest->Unmap(); + return dest; +} + void gfxUtils::ConvertBGRAtoRGBA(gfxImageSurface *aSourceSurface, gfxImageSurface *aDestSurface) { if (!aDestSurface) aDestSurface = aSourceSurface; MOZ_ASSERT(aSourceSurface->Format() == aDestSurface->Format() && aSourceSurface->Width() == aDestSurface->Width() &&
--- a/gfx/thebes/gfxUtils.h +++ b/gfx/thebes/gfxUtils.h @@ -39,16 +39,17 @@ public: * * If the source is not gfxImageFormat::ARGB32, no operation is performed. If * aDestSurface is given, the data is copied over. */ static void PremultiplyImageSurface(gfxImageSurface *aSourceSurface, gfxImageSurface *aDestSurface = nullptr); static void UnpremultiplyImageSurface(gfxImageSurface *aSurface, gfxImageSurface *aDestSurface = nullptr); + static mozilla::TemporaryRef<DataSourceSurface> UnpremultiplyDataSurface(DataSourceSurface* aSurface); static void ConvertBGRAtoRGBA(gfxImageSurface *aSourceSurface, gfxImageSurface *aDestSurface = nullptr); /** * Draw something drawable while working around limitations like bad support * for EXTEND_PAD, lack of source-clipping, or cairo / pixman bugs with * extreme user-space-to-image-space transforms.
--- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -5382,18 +5382,20 @@ nsLayoutUtils::SurfaceFromElement(nsIIma uint32_t noRasterize = aSurfaceFlags & SFE_NO_RASTERIZING_VECTORS; uint32_t whichFrame = (aSurfaceFlags & SFE_WANT_FIRST_FRAME) ? (uint32_t) imgIContainer::FRAME_FIRST : (uint32_t) imgIContainer::FRAME_CURRENT; uint32_t frameFlags = imgIContainer::FLAG_SYNC_DECODE; if (aSurfaceFlags & SFE_NO_COLORSPACE_CONVERSION) frameFlags |= imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION; - if (aSurfaceFlags & SFE_NO_PREMULTIPLY_ALPHA) + if (aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) { frameFlags |= imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA; + result.mIsPremultiplied = false; + } int32_t imgWidth, imgHeight; rv = imgContainer->GetWidth(&imgWidth); nsresult rv2 = imgContainer->GetHeight(&imgHeight); if (NS_FAILED(rv) || NS_FAILED(rv2)) return result; if (!noRasterize || imgContainer->GetType() == imgIContainer::TYPE_RASTER) { @@ -5445,62 +5447,38 @@ nsLayoutUtils::SurfaceFromElement(HTMLIm } nsLayoutUtils::SurfaceFromElementResult nsLayoutUtils::SurfaceFromElement(HTMLCanvasElement* aElement, uint32_t aSurfaceFlags, DrawTarget* aTarget) { SurfaceFromElementResult result; - nsresult rv; - - bool premultAlpha = (aSurfaceFlags & SFE_NO_PREMULTIPLY_ALPHA) == 0; + + bool* isPremultiplied = nullptr; + if (aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) { + isPremultiplied = &result.mIsPremultiplied; + } gfxIntSize size = aElement->GetSize(); - if (premultAlpha && aElement->CountContexts() == 1) { - nsICanvasRenderingContextInternal *srcCanvas = aElement->GetContextAtIndex(0); - result.mSourceSurface = srcCanvas->GetSurfaceSnapshot(); - } - + result.mSourceSurface = aElement->GetSurfaceSnapshot(isPremultiplied); if (!result.mSourceSurface) { - nsRefPtr<gfxContext> ctx; - RefPtr<DrawTarget> dt; - if (premultAlpha) { - if (aTarget) { - dt = aTarget->CreateSimilarDrawTarget(IntSize(size.width, size.height), SurfaceFormat::B8G8R8A8); - } else { - dt = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize(size.width, size.height), - SurfaceFormat::B8G8R8A8); - } - if (!dt) { - return result; - } - ctx = new gfxContext(dt); - } else { - // TODO: RenderContextsExternal expects to get a gfxImageFormat - // so that it can un-premultiply. - RefPtr<DataSourceSurface> data = Factory::CreateDataSourceSurface(IntSize(size.width, size.height), - SurfaceFormat::B8G8R8A8); - memset(data->GetData(), 0, data->Stride() * size.height); - result.mSourceSurface = data; - nsRefPtr<gfxImageSurface> image = new gfxImageSurface(data->GetData(), - gfxIntSize(size.width, size.height), - data->Stride(), - gfxImageFormat::ARGB32); - ctx = new gfxContext(image); - } - // XXX shouldn't use the external interface, but maybe we can layerify this - uint32_t flags = premultAlpha ? HTMLCanvasElement::RenderFlagPremultAlpha : 0; - rv = aElement->RenderContextsExternal(ctx, GraphicsFilter::FILTER_NEAREST, flags); - if (NS_FAILED(rv)) - return result; - - if (premultAlpha) { - 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. + DrawTarget *ref = aTarget ? aTarget : 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, // in case this is being used by -moz-element() aElement->MarkContextClean(); result.mSize = size; @@ -5512,17 +5490,17 @@ nsLayoutUtils::SurfaceFromElement(HTMLCa nsLayoutUtils::SurfaceFromElementResult nsLayoutUtils::SurfaceFromElement(HTMLVideoElement* aElement, uint32_t aSurfaceFlags, DrawTarget* aTarget) { SurfaceFromElementResult result; - NS_WARN_IF_FALSE((aSurfaceFlags & SFE_NO_PREMULTIPLY_ALPHA) == 0, "We can't support non-premultiplied alpha for video!"); + NS_WARN_IF_FALSE((aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) == 0, "We can't support non-premultiplied alpha for video!"); uint16_t readyState; if (NS_SUCCEEDED(aElement->GetReadyState(&readyState)) && (readyState == nsIDOMHTMLMediaElement::HAVE_NOTHING || readyState == nsIDOMHTMLMediaElement::HAVE_METADATA)) { result.mIsStillLoading = true; return result; } @@ -5536,16 +5514,23 @@ nsLayoutUtils::SurfaceFromElement(HTMLVi if (!container) return result; mozilla::gfx::IntSize size; result.mSourceSurface = container->GetCurrentAsSourceSurface(&size); if (!result.mSourceSurface) return result; + if (aTarget) { + RefPtr<SourceSurface> opt = aTarget->OptimizeSourceSurface(result.mSourceSurface); + if (opt) { + result.mSourceSurface = opt; + } + } + result.mCORSUsed = aElement->GetCORSMode() != CORS_NONE; result.mSize = ThebesIntSize(size); result.mPrincipal = principal.forget(); result.mIsWriteOnly = false; return result; } @@ -6504,17 +6489,20 @@ nsLayoutUtils::WantSubAPZC() wantSubAPZC = false; } #endif return wantSubAPZC; } nsLayoutUtils::SurfaceFromElementResult::SurfaceFromElementResult() // Use safe default values here - : mIsWriteOnly(true), mIsStillLoading(false), mCORSUsed(false) + : mIsWriteOnly(true) + , mIsStillLoading(false) + , mCORSUsed(false) + , mIsPremultiplied(true) { } bool nsLayoutUtils::IsNonWrapperBlock(nsIFrame* aFrame) { return GetAsBlock(aFrame) && !aFrame->IsBlockWrapper(); }
--- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -1690,20 +1690,20 @@ 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 = 1 << 1, /* Whether we should skip colorspace/gamma conversion */ SFE_NO_COLORSPACE_CONVERSION = 1 << 2, - /* Whether we should skip premultiplication -- the resulting - image will always be an image surface, and must not be given to - Thebes for compositing! */ - SFE_NO_PREMULTIPLY_ALPHA = 1 << 3, + /* 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. */ + 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 }; struct DirectDrawInfo { /* imgIContainer to directly draw to a context */ nsCOMPtr<imgIContainer> mImgContainer; @@ -1731,16 +1731,18 @@ public: nsCOMPtr<imgIRequest> mImageRequest; /* Whether the element was "write only", that is, the bits should not be exposed to content */ bool mIsWriteOnly; /* Whether the element was still loading. Some consumers need to handle this case specially. */ bool mIsStillLoading; /* Whether the element used CORS when loading. */ bool mCORSUsed; + /* Whether the returned image contains premultiplied pixel data */ + bool mIsPremultiplied; }; static SurfaceFromElementResult SurfaceFromElement(mozilla::dom::Element *aElement, uint32_t aSurfaceFlags = 0, DrawTarget *aTarget = nullptr); static SurfaceFromElementResult SurfaceFromElement(nsIImageLoadingContent *aElement, uint32_t aSurfaceFlags = 0, DrawTarget *aTarget = nullptr);