mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 23:31:56 +00:00
Bug 1616715 - SurfaceFromElement gets alpha Premult unless opt-in to NonPremult. r=lsalzman
Differential Revision: https://phabricator.services.mozilla.com/D63421 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
74a66ab98d
commit
011c0a1fd7
@ -18,6 +18,7 @@
|
||||
#include "mozilla/layers/OOPCanvasRenderer.h"
|
||||
#include "mozilla/layers/TextureClientSharedSurface.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/ScopeExit.h"
|
||||
#include "mozilla/StaticPrefs_webgl.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIGfxInfo.h"
|
||||
@ -883,9 +884,106 @@ ClientWebGLContext::SetContextOptions(JSContext* cx,
|
||||
void ClientWebGLContext::DidRefresh() { Run<RPROC(DidRefresh)>(); }
|
||||
|
||||
already_AddRefed<gfx::SourceSurface> ClientWebGLContext::GetSurfaceSnapshot(
|
||||
gfxAlphaType* out_alphaType) {
|
||||
auto ret = Run<RPROC(GetSurfaceSnapshot)>(out_alphaType);
|
||||
return ret.forget();
|
||||
gfxAlphaType* const out_alphaType) {
|
||||
const FuncScope funcScope(*this, "<GetSurfaceSnapshot>");
|
||||
if (IsContextLost()) return nullptr;
|
||||
const auto notLost =
|
||||
mNotLost; // Hold a strong-ref to prevent LoseContext=>UAF.
|
||||
|
||||
const auto& options = mNotLost->info.options;
|
||||
const auto& state = State();
|
||||
|
||||
const auto drawFbWas = state.mBoundDrawFb;
|
||||
const auto readFbWas = state.mBoundReadFb;
|
||||
|
||||
const auto alignmentWas = Run<RPROC(GetParameter)>(LOCAL_GL_PACK_ALIGNMENT);
|
||||
if (!alignmentWas) return nullptr;
|
||||
|
||||
const auto size = DrawingBufferSize();
|
||||
|
||||
// -
|
||||
|
||||
BindFramebuffer(LOCAL_GL_FRAMEBUFFER, nullptr);
|
||||
PixelStorei(LOCAL_GL_PACK_ALIGNMENT, 4);
|
||||
|
||||
auto reset = MakeScopeExit([&] {
|
||||
if (drawFbWas == readFbWas) {
|
||||
BindFramebuffer(LOCAL_GL_FRAMEBUFFER, drawFbWas);
|
||||
} else {
|
||||
BindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, drawFbWas);
|
||||
BindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, readFbWas);
|
||||
}
|
||||
PixelStorei(LOCAL_GL_PACK_ALIGNMENT, *alignmentWas);
|
||||
});
|
||||
|
||||
const auto surfFormat = options.alpha ? gfx::SurfaceFormat::B8G8R8A8
|
||||
: gfx::SurfaceFormat::B8G8R8X8;
|
||||
const auto stride = size.x * 4;
|
||||
RefPtr<gfx::DataSourceSurface> surf =
|
||||
gfx::Factory::CreateDataSourceSurfaceWithStride(
|
||||
{size.x, size.y}, surfFormat, stride, /*zero=*/true);
|
||||
MOZ_ASSERT(surf);
|
||||
if (NS_WARN_IF(!surf)) return nullptr;
|
||||
|
||||
{
|
||||
const gfx::DataSourceSurface::ScopedMap map(
|
||||
surf, gfx::DataSourceSurface::READ_WRITE);
|
||||
if (!map.IsMapped()) {
|
||||
MOZ_ASSERT(false);
|
||||
return nullptr;
|
||||
}
|
||||
MOZ_ASSERT(static_cast<uint32_t>(map.GetStride()) == stride);
|
||||
|
||||
const auto range = Range<uint8_t>(map.GetData(), stride * size.y);
|
||||
auto view = RawBufferView(range);
|
||||
Run<RPROC(ReadPixels)>(0, 0, size.x, size.y, LOCAL_GL_RGBA,
|
||||
LOCAL_GL_UNSIGNED_BYTE, view);
|
||||
|
||||
// -
|
||||
|
||||
const auto swapRowRedBlue = [&](uint8_t* const row) {
|
||||
for (const auto x : IntegerRange(size.x)) {
|
||||
std::swap(row[4 * x], row[4 * x + 2]);
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<uint8_t> tempRow(stride);
|
||||
for (const auto srcY : IntegerRange(size.y / 2)) {
|
||||
const auto dstY = size.y - 1 - srcY;
|
||||
const auto srcRow = (range.begin() + (stride * srcY)).get();
|
||||
const auto dstRow = (range.begin() + (stride * dstY)).get();
|
||||
memcpy(tempRow.data(), dstRow, stride);
|
||||
memcpy(dstRow, srcRow, stride);
|
||||
swapRowRedBlue(dstRow);
|
||||
memcpy(srcRow, tempRow.data(), stride);
|
||||
swapRowRedBlue(srcRow);
|
||||
}
|
||||
if (size.y & 1) {
|
||||
const auto midY = size.y / 2; // size.y = 3 => midY = 1
|
||||
const auto midRow = (range.begin() + (stride * midY)).get();
|
||||
swapRowRedBlue(midRow);
|
||||
}
|
||||
}
|
||||
|
||||
gfxAlphaType srcAlphaType;
|
||||
if (!options.alpha) {
|
||||
srcAlphaType = gfxAlphaType::Opaque;
|
||||
} else if (options.premultipliedAlpha) {
|
||||
srcAlphaType = gfxAlphaType::Premult;
|
||||
} else {
|
||||
srcAlphaType = gfxAlphaType::NonPremult;
|
||||
}
|
||||
|
||||
if (out_alphaType) {
|
||||
*out_alphaType = srcAlphaType;
|
||||
} else {
|
||||
// Expects Opaque or Premult
|
||||
if (srcAlphaType == gfxAlphaType::NonPremult) {
|
||||
gfxUtils::PremultiplyDataSurface(surf, surf);
|
||||
}
|
||||
}
|
||||
|
||||
return surf.forget();
|
||||
}
|
||||
|
||||
UniquePtr<uint8_t[]> ClientWebGLContext::GetImageBuffer(int32_t* out_format) {
|
||||
|
@ -196,11 +196,6 @@ class HostWebGLContext final : public SupportsWeakPtr<HostWebGLContext> {
|
||||
|
||||
void DidRefresh() { mContext->DidRefresh(); }
|
||||
|
||||
RefPtr<gfx::SourceSurface> GetSurfaceSnapshot(
|
||||
gfxAlphaType* out_alphaType) const {
|
||||
return mContext->GetSurfaceSnapshot(out_alphaType);
|
||||
}
|
||||
|
||||
void GenerateError(const GLenum error, const std::string& text) const {
|
||||
mContext->GenerateErrorImpl(error, text);
|
||||
}
|
||||
|
@ -1252,56 +1252,6 @@ void WebGLContext::LoseContext(const webgl::ContextLossReason reason) {
|
||||
mHost->OnContextLoss(reason);
|
||||
}
|
||||
|
||||
RefPtr<gfx::SourceSurface> WebGLContext::GetSurfaceSnapshot(
|
||||
gfxAlphaType* const out_alphaType) {
|
||||
const FuncScope funcScope(*this, "<GetSurfaceSnapshot>");
|
||||
if (IsContextLost()) return nullptr;
|
||||
|
||||
if (!BindDefaultFBForRead()) return nullptr;
|
||||
|
||||
const auto surfFormat = mOptions.alpha ? gfx::SurfaceFormat::B8G8R8A8
|
||||
: gfx::SurfaceFormat::B8G8R8X8;
|
||||
const auto& size = mDefaultFB->mSize;
|
||||
RefPtr<gfx::DataSourceSurface> surf;
|
||||
surf = gfx::Factory::CreateDataSourceSurfaceWithStride(size, surfFormat,
|
||||
size.width * 4);
|
||||
if (NS_WARN_IF(!surf)) return nullptr;
|
||||
|
||||
ReadPixelsIntoDataSurface(gl, surf);
|
||||
|
||||
gfxAlphaType alphaType;
|
||||
if (!mOptions.alpha) {
|
||||
alphaType = gfxAlphaType::Opaque;
|
||||
} else if (mOptions.premultipliedAlpha) {
|
||||
alphaType = gfxAlphaType::Premult;
|
||||
} else {
|
||||
alphaType = gfxAlphaType::NonPremult;
|
||||
}
|
||||
|
||||
if (out_alphaType) {
|
||||
*out_alphaType = alphaType;
|
||||
} else {
|
||||
// Expects Opaque or Premult
|
||||
if (alphaType == gfxAlphaType::NonPremult) {
|
||||
gfxUtils::PremultiplyDataSurface(surf, surf);
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateDrawTarget(
|
||||
gfxPlatform::GetPlatform()->GetSoftwareBackend(), size,
|
||||
gfx::SurfaceFormat::B8G8R8A8);
|
||||
if (!dt) return nullptr;
|
||||
|
||||
dt->SetTransform(
|
||||
gfx::Matrix::Translation(0.0, size.height).PreScale(1.0, -1.0));
|
||||
|
||||
const gfx::Rect rect{0, 0, float(size.width), float(size.height)};
|
||||
dt->DrawSurface(surf, rect, rect, gfx::DrawSurfaceOptions(),
|
||||
gfx::DrawOptions(1.0f, gfx::CompositionOp::OP_SOURCE));
|
||||
|
||||
return dt->Snapshot();
|
||||
}
|
||||
|
||||
void WebGLContext::DidRefresh() {
|
||||
if (gl) {
|
||||
gl->FlushIfHeavyGLCallsSinceLastFlush();
|
||||
|
@ -374,9 +374,6 @@ class WebGLContext : public VRefCounted, public SupportsWeakPtr<WebGLContext> {
|
||||
|
||||
void SetCompositableHost(RefPtr<layers::CompositableHost>& aCompositableHost);
|
||||
|
||||
RefPtr<mozilla::gfx::SourceSurface> GetSurfaceSnapshot(
|
||||
gfxAlphaType* out_alphaType);
|
||||
|
||||
/**
|
||||
* An abstract base class to be implemented by callers wanting to be notified
|
||||
* that a refresh has occurred. Callers must ensure an observer is removed
|
||||
|
@ -52,7 +52,6 @@ DEFINE_ASYNC(HostWebGLContext::DeleteTransformFeedback)
|
||||
DEFINE_ASYNC(HostWebGLContext::DeleteVertexArray)
|
||||
|
||||
DEFINE_ASYNC(HostWebGLContext::ClearVRFrame)
|
||||
DEFINE_SYNC(HostWebGLContext::GetSurfaceSnapshot)
|
||||
DEFINE_SYNC(HostWebGLContext::GetVRFrame)
|
||||
|
||||
DEFINE_ASYNC(HostWebGLContext::Disable)
|
||||
|
@ -170,14 +170,12 @@ UniquePtr<webgl::TexUnpackBlob> WebGLContext::FromDomElem(
|
||||
// same as drawImage.
|
||||
uint32_t flags = nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE |
|
||||
nsLayoutUtils::SFE_WANT_IMAGE_SURFACE |
|
||||
nsLayoutUtils::SFE_USE_ELEMENT_SIZE_IF_VECTOR;
|
||||
nsLayoutUtils::SFE_USE_ELEMENT_SIZE_IF_VECTOR |
|
||||
nsLayoutUtils::SFE_ALLOW_NON_PREMULT;
|
||||
|
||||
if (mPixelStore.mColorspaceConversion == LOCAL_GL_NONE)
|
||||
flags |= nsLayoutUtils::SFE_NO_COLORSPACE_CONVERSION;
|
||||
|
||||
if (!mPixelStore.mPremultiplyAlpha)
|
||||
flags |= nsLayoutUtils::SFE_PREFER_NO_PREMULTIPLY_ALPHA;
|
||||
|
||||
RefPtr<gfx::DrawTarget> idealDrawTarget = nullptr; // Don't care for now.
|
||||
auto sfer = nsLayoutUtils::SurfaceFromElement(
|
||||
const_cast<dom::Element*>(&elem), flags, idealDrawTarget);
|
||||
|
@ -7580,7 +7580,7 @@ nsLayoutUtils::SurfaceFromElementResult nsLayoutUtils::SurfaceFromElement(
|
||||
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) {
|
||||
if (aSurfaceFlags & SFE_ALLOW_NON_PREMULT) {
|
||||
frameFlags |= imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
|
||||
}
|
||||
|
||||
@ -7666,7 +7666,12 @@ nsLayoutUtils::SurfaceFromElementResult nsLayoutUtils::SurfaceFromElement(
|
||||
|
||||
IntSize size = aElement->GetSize();
|
||||
|
||||
result.mSourceSurface = aElement->GetSurfaceSnapshot(&result.mAlphaType);
|
||||
auto pAlphaType = &result.mAlphaType;
|
||||
if (!(aSurfaceFlags & SFE_ALLOW_NON_PREMULT)) {
|
||||
pAlphaType =
|
||||
nullptr; // Coersce GetSurfaceSnapshot to give us Opaque/Premult only.
|
||||
}
|
||||
result.mSourceSurface = aElement->GetSurfaceSnapshot(pAlphaType);
|
||||
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
|
||||
|
@ -2125,9 +2125,8 @@ class nsLayoutUtils {
|
||||
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 either OPAQUE or NON_PREMULT mAlphaType,
|
||||
if this is can be done efficiently. */
|
||||
SFE_PREFER_NO_PREMULTIPLY_ALPHA = 1 << 3,
|
||||
/* Caller handles SFER::mAlphaType = NonPremult */
|
||||
SFE_ALLOW_NON_PREMULT = 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,
|
||||
|
Loading…
Reference in New Issue
Block a user