Add full mix-blend mode support to the OpenGL compositor. (bug 1235995 part 1, r=mstange)

This commit is contained in:
David Anderson 2016-01-19 13:24:19 +07:00
parent 8fcc59449e
commit cf1d101569
8 changed files with 423 additions and 35 deletions

View File

@ -558,6 +558,31 @@ size_t DecomposeIntoNoRepeatRects(const gfx::Rect& aRect,
decomposedRectArrayT* aLayerRects,
decomposedRectArrayT* aTextureRects);
static inline bool
BlendOpIsMixBlendMode(gfx::CompositionOp aOp)
{
switch (aOp) {
case gfx::CompositionOp::OP_MULTIPLY:
case gfx::CompositionOp::OP_SCREEN:
case gfx::CompositionOp::OP_OVERLAY:
case gfx::CompositionOp::OP_DARKEN:
case gfx::CompositionOp::OP_LIGHTEN:
case gfx::CompositionOp::OP_COLOR_DODGE:
case gfx::CompositionOp::OP_COLOR_BURN:
case gfx::CompositionOp::OP_HARD_LIGHT:
case gfx::CompositionOp::OP_SOFT_LIGHT:
case gfx::CompositionOp::OP_DIFFERENCE:
case gfx::CompositionOp::OP_EXCLUSION:
case gfx::CompositionOp::OP_HUE:
case gfx::CompositionOp::OP_SATURATION:
case gfx::CompositionOp::OP_COLOR:
case gfx::CompositionOp::OP_LUMINOSITY:
return true;
default:
return false;
}
}
} // namespace layers
} // namespace mozilla

View File

@ -71,6 +71,18 @@ BindMaskForProgram(ShaderProgramOGL* aProgram, TextureSourceOGL* aSourceMask,
aProgram->SetMaskLayerTransform(aTransform);
}
void
CompositorOGL::BindBackdrop(ShaderProgramOGL* aProgram, GLuint aBackdrop, GLenum aTexUnit)
{
MOZ_ASSERT(aBackdrop);
mGLContext->fActiveTexture(aTexUnit);
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, aBackdrop);
mGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR);
mGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR);
aProgram->SetBackdropTextureUnit(aTexUnit - LOCAL_GL_TEXTURE0);
}
CompositorOGL::CompositorOGL(nsIWidget *aWidget, int aSurfaceWidth,
int aSurfaceHeight, bool aUseExternalSurfaceSize)
: mWidget(aWidget)
@ -707,9 +719,16 @@ CompositorOGL::BeginFrame(const nsIntRegion& aInvalidRegion,
}
void
CompositorOGL::CreateFBOWithTexture(const IntRect& aRect, bool aCopyFromSource,
CompositorOGL::CreateFBOWithTexture(const gfx::IntRect& aRect, bool aCopyFromSource,
GLuint aSourceFrameBuffer,
GLuint *aFBO, GLuint *aTexture)
{
*aTexture = CreateTexture(aRect, aCopyFromSource, aSourceFrameBuffer);
mGLContext->fGenFramebuffers(1, aFBO);
}
GLuint
CompositorOGL::CreateTexture(const IntRect& aRect, bool aCopyFromSource, GLuint aSourceFrameBuffer)
{
// we're about to create a framebuffer backed by textures to use as an intermediate
// surface. What to do if its size (as given by aRect) would exceed the
@ -722,7 +741,7 @@ CompositorOGL::CreateFBOWithTexture(const IntRect& aRect, bool aCopyFromSource,
clampedRect.width = std::min(clampedRect.width, maxTexSize);
clampedRect.height = std::min(clampedRect.height, maxTexSize);
GLuint tex, fbo;
GLuint tex;
mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);
mGLContext->fGenTextures(1, &tex);
@ -774,6 +793,7 @@ CompositorOGL::CreateFBOWithTexture(const IntRect& aRect, bool aCopyFromSource,
LOCAL_GL_UNSIGNED_BYTE,
buf.get());
}
GLenum error = mGLContext->fGetError();
if (error != LOCAL_GL_NO_ERROR) {
nsAutoCString msg;
@ -801,10 +821,7 @@ CompositorOGL::CreateFBOWithTexture(const IntRect& aRect, bool aCopyFromSource,
LOCAL_GL_CLAMP_TO_EDGE);
mGLContext->fBindTexture(mFBOTextureTarget, 0);
mGLContext->fGenFramebuffers(1, &fbo);
*aFBO = fbo;
*aTexture = tex;
return tex;
}
ShaderConfigOGL
@ -855,6 +872,9 @@ CompositorOGL::GetShaderConfigFor(Effect *aEffect,
source->GetFormat() == gfx::SurfaceFormat::R5G6B5_UINT16);
config = ShaderConfigFromTargetAndFormat(source->GetTextureTarget(),
source->GetFormat());
if (!texturedEffect->mPremultiplied) {
config.SetNoPremultipliedAlpha();
}
break;
}
}
@ -862,6 +882,7 @@ CompositorOGL::GetShaderConfigFor(Effect *aEffect,
config.SetMask2D(aMask == MaskType::Mask2d);
config.SetMask3D(aMask == MaskType::Mask3d);
config.SetDEAA(aDEAAEnabled);
config.SetCompositionOp(aOp);
return config;
}
@ -898,10 +919,15 @@ CompositorOGL::ResetProgram()
mCurrentProgram = nullptr;
}
static bool SetBlendMode(GLContext* aGL, gfx::CompositionOp aBlendMode, bool aIsPremultiplied = true)
{
if (BlendOpIsMixBlendMode(aBlendMode)) {
// Mix-blend modes require an extra step (or more) that cannot be expressed
// in the fixed-function blending capabilities of opengl. We handle them
// separately in shaders, and the shaders assume we will use our default
// blend function for compositing (premultiplied OP_OVER).
return false;
}
if (aBlendMode == gfx::CompositionOp::OP_OVER && aIsPremultiplied) {
return false;
}
@ -1066,11 +1092,18 @@ CompositorOGL::DrawQuad(const Rect& aRect,
aOpacity = 1.f;
}
GLuint mixBlendBackdrop = 0;
gfx::CompositionOp blendMode = gfx::CompositionOp::OP_OVER;
if (aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE]) {
EffectBlendMode *blendEffect =
static_cast<EffectBlendMode*>(aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE].get());
blendMode = blendEffect->mBlendMode;
if (BlendOpIsMixBlendMode(blendMode)) {
gfx::IntRect rect(gfx::IntPoint(0, 0), mCurrentRenderTarget->GetSize());
mixBlendBackdrop = CreateTexture(rect, true, mCurrentRenderTarget->GetFBO());
}
}
// Only apply DEAA to quads that have been transformed such that aliasing
@ -1184,6 +1217,9 @@ CompositorOGL::DrawQuad(const Rect& aRect,
if (maskType != MaskType::MaskNone) {
BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE0, maskQuadTransform);
}
if (mixBlendBackdrop) {
BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE1);
}
didSetBlendMode = SetBlendMode(gl(), blendMode);
@ -1221,6 +1257,9 @@ CompositorOGL::DrawQuad(const Rect& aRect,
if (maskType != MaskType::MaskNone) {
BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE1, maskQuadTransform);
}
if (mixBlendBackdrop) {
BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE2);
}
BindAndDrawQuadWithTextureRect(program, aRect, texturedEffect->mTextureCoords, source);
}
@ -1249,6 +1288,9 @@ CompositorOGL::DrawQuad(const Rect& aRect,
if (maskType != MaskType::MaskNone) {
BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE3, maskQuadTransform);
}
if (mixBlendBackdrop) {
BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE4);
}
didSetBlendMode = SetBlendMode(gl(), blendMode);
BindAndDrawQuadWithTextureRect(program,
aRect,
@ -1283,6 +1325,9 @@ CompositorOGL::DrawQuad(const Rect& aRect,
if (maskType != MaskType::MaskNone) {
BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE2, maskQuadTransform);
}
if (mixBlendBackdrop) {
BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE3);
}
didSetBlendMode = SetBlendMode(gl(), blendMode);
BindAndDrawQuadWithTextureRect(program,
aRect,
@ -1309,6 +1354,9 @@ CompositorOGL::DrawQuad(const Rect& aRect,
if (maskType != MaskType::MaskNone) {
BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE1, maskQuadTransform);
}
if (mixBlendBackdrop) {
BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE2);
}
if (config.mFeatures & ENABLE_TEXTURE_RECT) {
// 2DRect case, get the multiplier right for a sampler2DRect
@ -1392,6 +1440,9 @@ CompositorOGL::DrawQuad(const Rect& aRect,
gl()->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA);
}
if (mixBlendBackdrop) {
gl()->fDeleteTextures(1, &mixBlendBackdrop);
}
// in case rendering has used some other GL context
MakeCurrent();
@ -1451,22 +1502,12 @@ CompositorOGL::EndFrame()
mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
// Unbind all textures
mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, 0);
if (!mGLContext->IsGLES()) {
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
}
mGLContext->fActiveTexture(LOCAL_GL_TEXTURE1);
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, 0);
if (!mGLContext->IsGLES()) {
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
}
mGLContext->fActiveTexture(LOCAL_GL_TEXTURE2);
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, 0);
if (!mGLContext->IsGLES()) {
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
for (GLuint i = 0; i <= 4; i++) {
mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, 0);
if (!mGLContext->IsGLES()) {
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
}
}
}

View File

@ -216,6 +216,11 @@ public:
mFBOTextureTarget == LOCAL_GL_TEXTURE_2D,
SupportsPartialTextureUpdate());
result.mSupportedBlendModes += gfx::CompositionOp::OP_SOURCE;
for (uint8_t op = 0; op < uint8_t(gfx::CompositionOp::OP_COUNT); op++) {
if (BlendOpIsMixBlendMode(gfx::CompositionOp(op))) {
result.mSupportedBlendModes += gfx::CompositionOp(op);
}
}
return result;
}
@ -422,6 +427,7 @@ private:
void CreateFBOWithTexture(const gfx::IntRect& aRect, bool aCopyFromSource,
GLuint aSourceFrameBuffer,
GLuint *aFBO, GLuint *aTexture);
GLuint CreateTexture(const gfx::IntRect& aRect, bool aCopyFromSource, GLuint aSourceFrameBuffer);
void BindAndDrawQuads(ShaderProgramOGL *aProg,
int aQuads,
@ -445,6 +451,12 @@ private:
void ActivateProgram(ShaderProgramOGL *aProg);
void CleanupResources();
/**
* Bind the texture behind the current render target as the backdrop for a
* mix-blend shader.
*/
void BindBackdrop(ShaderProgramOGL* aProgram, GLuint aBackdrop, GLenum aTexUnit);
/**
* Copies the content of our backbuffer to the set transaction target.
* Does not restore the target FBO, so only call from EndFrame.

View File

@ -43,6 +43,7 @@ AddUniforms(ProgramProfileOGL& aProfile)
"uBlackTexture",
"uWhiteTexture",
"uMaskTexture",
"uBackdropTexture",
"uRenderColor",
"uTexCoordMultiplier",
"uCbCrTexCoordMultiplier",
@ -148,9 +149,9 @@ ShaderConfigOGL::SetMask3D(bool aEnabled)
}
void
ShaderConfigOGL::SetPremultiply(bool aEnabled)
ShaderConfigOGL::SetNoPremultipliedAlpha()
{
SetFeature(ENABLE_PREMULTIPLY, aEnabled);
SetFeature(ENABLE_NO_PREMUL_ALPHA, true);
}
void
@ -159,6 +160,12 @@ ShaderConfigOGL::SetDEAA(bool aEnabled)
SetFeature(ENABLE_DEAA, aEnabled);
}
void
ShaderConfigOGL::SetCompositionOp(CompositionOp aOp)
{
mCompositionOp = aOp;
}
/* static */ ProgramProfileOGL
ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig)
{
@ -167,6 +174,8 @@ ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig)
AddUniforms(result);
CompositionOp blendOp = aConfig.mCompositionOp;
vs << "#ifdef GL_ES" << endl;
vs << "#define EDGE_PRECISION mediump" << endl;
vs << "#else" << endl;
@ -190,6 +199,10 @@ ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig)
vs << "varying vec2 vTexCoord;" << endl;
}
if (BlendOpIsMixBlendMode(blendOp)) {
vs << "varying vec2 vBackdropCoord;" << endl;
}
if (aConfig.mFeatures & ENABLE_MASK_2D ||
aConfig.mFeatures & ENABLE_MASK_3D) {
vs << "uniform mat4 uMaskTransform;" << endl;
@ -273,6 +286,10 @@ ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig)
}
vs << " finalPosition.xy -= uRenderTargetOffset * finalPosition.w;" << endl;
vs << " finalPosition = uMatrixProj * finalPosition;" << endl;
if (BlendOpIsMixBlendMode(blendOp)) {
// Move from clip space coordinates into texture/uv-coordinates.
vs << " vBackdropCoord = (finalPosition.xy + vec2(1.0, 1.0)) / 2.0;" << endl;
}
vs << " gl_Position = finalPosition;" << endl;
vs << "}" << endl;
@ -309,6 +326,9 @@ ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig)
fs << "uniform COLOR_PRECISION float uLayerOpacity;" << endl;
}
}
if (BlendOpIsMixBlendMode(blendOp)) {
fs << "varying vec2 vBackdropCoord;" << endl;
}
const char *sampler2D = "sampler2D";
const char *texture2D = "texture2D";
@ -342,6 +362,13 @@ ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig)
fs << "uniform " << sampler2D << " uTexture;" << endl;
}
if (BlendOpIsMixBlendMode(blendOp)) {
// Component alpha should be flattened away inside blend containers.
MOZ_ASSERT(!(aConfig.mFeatures & ENABLE_TEXTURE_COMPONENT_ALPHA));
fs << "uniform sampler2D uBackdropTexture;" << endl;
}
if (aConfig.mFeatures & ENABLE_MASK_2D ||
aConfig.mFeatures & ENABLE_MASK_3D) {
fs << "varying vec3 vMaskCoord;" << endl;
@ -352,6 +379,10 @@ ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig)
fs << "uniform EDGE_PRECISION vec3 uSSEdges[4];" << endl;
}
if (BlendOpIsMixBlendMode(blendOp)) {
BuildMixBlender(aConfig, fs);
}
if (!(aConfig.mFeatures & ENABLE_RENDER_COLOR)) {
fs << "vec4 sample(vec2 coord) {" << endl;
fs << " vec4 color;" << endl;
@ -459,9 +490,6 @@ For [0,1] instead of [0,255], and to 5 places:
if (aConfig.mFeatures & ENABLE_OPACITY) {
fs << " color *= uLayerOpacity;" << endl;
}
if (aConfig.mFeatures & ENABLE_PREMULTIPLY) {
fs << " color.rgb *= color.a;" << endl;
}
}
if (aConfig.mFeatures & ENABLE_DEAA) {
// Calculate the sub-pixel coverage of the pixel and modulate its opacity
@ -473,6 +501,10 @@ For [0,1] instead of [0,255], and to 5 places:
fs << " deaaCoverage *= clamp(dot(uSSEdges[3], ssPos), 0.0, 1.0);" << endl;
fs << " color *= deaaCoverage;" << endl;
}
if (BlendOpIsMixBlendMode(blendOp)) {
fs << " vec4 backdrop = texture2D(uBackdropTexture, vBackdropCoord);" << endl;
fs << " color = mixAndBlend(backdrop, color);" << endl;
}
if (aConfig.mFeatures & ENABLE_MASK_3D) {
fs << " vec2 maskCoords = vMaskCoord.xy / vMaskCoord.z;" << endl;
fs << " COLOR_PRECISION float mask = texture2D(uMaskTexture, maskCoords).r;" << endl;
@ -507,10 +539,226 @@ For [0,1] instead of [0,255], and to 5 places:
aConfig.mFeatures & ENABLE_MASK_3D) {
result.mTextureCount = 1;
}
if (BlendOpIsMixBlendMode(blendOp)) {
result.mTextureCount += 1;
}
return result;
}
void
ProgramProfileOGL::BuildMixBlender(const ShaderConfigOGL& aConfig, std::ostringstream& fs)
{
// From the "Compositing and Blending Level 1" spec.
// Generate helper functions first.
switch (aConfig.mCompositionOp) {
case gfx::CompositionOp::OP_OVERLAY:
case gfx::CompositionOp::OP_HARD_LIGHT:
// Note: we substitute (2*src-1) into the screen formula below.
fs << "float hardlight(float dest, float src) {" << endl;
fs << " if (src <= 0.5) {" << endl;
fs << " return dest * (2.0 * src);" << endl;
fs << " } else {" << endl;
fs << " return 2.0*dest + 2.0*src - 1.0 - 2.0*dest*src;" << endl;
fs << " }" << endl;
fs << "}" << endl;
break;
case gfx::CompositionOp::OP_COLOR_DODGE:
fs << "float dodge(float dest, float src) {" << endl;
fs << " if (dest == 0.0) {" << endl;
fs << " return 0.0;" << endl;
fs << " } else if (src == 1.0) {" << endl;
fs << " return 1.0;" << endl;
fs << " } else {" << endl;
fs << " return min(1.0, dest / (1.0 - src));" << endl;
fs << " }" << endl;
fs << "}" << endl;
break;
case gfx::CompositionOp::OP_COLOR_BURN:
fs << "float burn(float dest, float src) {" << endl;
fs << " if (dest == 1.0) {" << endl;
fs << " return 1.0;" << endl;
fs << " } else if (src == 0.0) {" << endl;
fs << " return 0.0;" << endl;
fs << " } else {" << endl;
fs << " return 1.0 - min(1.0, (1.0 - dest) / src);" << endl;
fs << " }" << endl;
fs << "}" << endl;
break;
case gfx::CompositionOp::OP_SOFT_LIGHT:
fs << "float darken(float dest) {" << endl;
fs << " if (dest <= 0.25) {" << endl;
fs << " return ((16.0 * dest - 12.0) * dest + 4.0) * dest;" << endl;
fs << " } else {" << endl;
fs << " return sqrt(dest);" << endl;
fs << " }" << endl;
fs << "}" << endl;
fs << "float softlight(float dest, float src) {" << endl;
fs << " if (src <= 0.5) {" << endl;
fs << " return dest - (1.0 - 2.0 * src) * dest * (1.0 - dest);" << endl;
fs << " } else {" << endl;
fs << " return dest + (2.0 * src - 1.0) * (darken(dest) - dest);" << endl;
fs << " }" << endl;
fs << "}" << endl;
break;
case gfx::CompositionOp::OP_HUE:
case gfx::CompositionOp::OP_SATURATION:
case gfx::CompositionOp::OP_COLOR:
case gfx::CompositionOp::OP_LUMINOSITY:
fs << "float Lum(vec3 c) {" << endl;
fs << " return dot(vec3(0.3, 0.59, 0.11), c);" << endl;
fs << "}" << endl;
fs << "vec3 ClipColor(vec3 c) {" << endl;
fs << " float L = Lum(c);" << endl;
fs << " float n = min(min(c.r, c.g), c.b);" << endl;
fs << " float x = max(max(c.r, c.g), c.b);" << endl;
fs << " if (n < 0.0) {" << endl;
fs << " c = L + (((c - L) * L) / (L - n));" << endl;
fs << " }" << endl;
fs << " if (x > 1.0) {" << endl;
fs << " c = L + (((c - L) * (1.0 - L)) / (x - L));" << endl;
fs << " }" << endl;
fs << " return c;" << endl;
fs << "}" << endl;
fs << "vec3 SetLum(vec3 c, float L) {" << endl;
fs << " float d = L - Lum(c);" << endl;
fs << " return ClipColor(vec3(" << endl;
fs << " c.r + d," << endl;
fs << " c.g + d," << endl;
fs << " c.b + d));" << endl;
fs << "}" << endl;
fs << "float Sat(vec3 c) {" << endl;
fs << " return max(max(c.r, c.g), c.b) - min(min(c.r, c.g), c.b);" << endl;
fs << "}" << endl;
// To use this helper, re-arrange rgb such that r=min, g=mid, and b=max.
fs << "vec3 SetSatInner(vec3 c, float s) {" << endl;
fs << " if (c.b > c.r) {" << endl;
fs << " c.g = (((c.g - c.r) * s) / (c.b - c.r));" << endl;
fs << " c.b = s;" << endl;
fs << " } else {" << endl;
fs << " c.gb = vec2(0.0, 0.0);" << endl;
fs << " }" << endl;
fs << " return vec3(0.0, c.gb);" << endl;
fs << "}" << endl;
fs << "vec3 SetSat(vec3 c, float s) {" << endl;
fs << " if (c.r <= c.g) {" << endl;
fs << " if (c.g <= c.b) {" << endl;
fs << " c.rgb = SetSatInner(c.rgb, s);" << endl;
fs << " } else if (c.r <= c.b) {" << endl;
fs << " c.rbg = SetSatInner(c.rbg, s);" << endl;
fs << " } else {" << endl;
fs << " c.brg = SetSatInner(c.brg, s);" << endl;
fs << " }" << endl;
fs << " } else if (c.r <= c.b) {" << endl;
fs << " c.grb = SetSatInner(c.grb, s);" << endl;
fs << " } else if (c.g <= c.b) {" << endl;
fs << " c.gbr = SetSatInner(c.gbr, s);" << endl;
fs << " } else {" << endl;
fs << " c.bgr = SetSatInner(c.bgr, s);" << endl;
fs << " }" << endl;
fs << " return c;" << endl;
fs << "}" << endl;
break;
default:
break;
}
// Generate the main blending helper.
fs << "vec3 blend(vec3 dest, vec3 src) {" << endl;
switch (aConfig.mCompositionOp) {
case gfx::CompositionOp::OP_MULTIPLY:
fs << " return dest * src;" << endl;
break;
case gfx::CompositionOp::OP_SCREEN:
fs << " return dest + src - (dest * src);" << endl;
break;
case gfx::CompositionOp::OP_OVERLAY:
fs << " return vec3(" << endl;
fs << " hardlight(src.r, dest.r)," << endl;
fs << " hardlight(src.g, dest.g)," << endl;
fs << " hardlight(src.b, dest.b));" << endl;
break;
case gfx::CompositionOp::OP_DARKEN:
fs << " return min(dest, src);" << endl;
break;
case gfx::CompositionOp::OP_LIGHTEN:
fs << " return max(dest, src);" << endl;
break;
case gfx::CompositionOp::OP_COLOR_DODGE:
fs << " return vec3(" << endl;
fs << " dodge(dest.r, src.r)," << endl;
fs << " dodge(dest.g, src.g)," << endl;
fs << " dodge(dest.b, src.b));" << endl;
break;
case gfx::CompositionOp::OP_COLOR_BURN:
fs << " return vec3(" << endl;
fs << " burn(dest.r, src.r)," << endl;
fs << " burn(dest.g, src.g)," << endl;
fs << " burn(dest.b, src.b));" << endl;
break;
case gfx::CompositionOp::OP_HARD_LIGHT:
fs << " return vec3(" << endl;
fs << " hardlight(dest.r, src.r)," << endl;
fs << " hardlight(dest.g, src.g)," << endl;
fs << " hardlight(dest.b, src.b));" << endl;
break;
case gfx::CompositionOp::OP_SOFT_LIGHT:
fs << " return vec3(" << endl;
fs << " softlight(dest.r, src.r)," << endl;
fs << " softlight(dest.g, src.g)," << endl;
fs << " softlight(dest.b, src.b));" << endl;
break;
case gfx::CompositionOp::OP_DIFFERENCE:
fs << " return abs(dest - src);" << endl;
break;
case gfx::CompositionOp::OP_EXCLUSION:
fs << " return dest + src - 2.0*dest*src;" << endl;
break;
case gfx::CompositionOp::OP_HUE:
fs << " return SetLum(SetSat(src, Sat(dest)), Lum(dest));" << endl;
break;
case gfx::CompositionOp::OP_SATURATION:
fs << " return SetLum(SetSat(dest, Sat(src)), Lum(dest));" << endl;
break;
case gfx::CompositionOp::OP_COLOR:
fs << " return SetLum(src, Lum(dest));" << endl;
break;
case gfx::CompositionOp::OP_LUMINOSITY:
fs << " return SetLum(dest, Lum(src));" << endl;
break;
default:
MOZ_ASSERT_UNREACHABLE("unknown blend mode");
}
fs << "}" << endl;
// Generate the mix-blend function the fragment shader will call.
fs << "vec4 mixAndBlend(vec4 backdrop, vec4 color) {" << endl;
// Shortcut when the backdrop or source alpha is 0, otherwise we may leak
// Infinity into the blend function and return incorrect results.
fs << " if (backdrop.a == 0.0) {" << endl;
fs << " return color;" << endl;
fs << " }" << endl;
fs << " if (color.a == 0.0) {" << endl;
fs << " return backdrop;" << endl;
fs << " }" << endl;
// The spec assumes there is no premultiplied alpha. The backdrop is always
// premultiplied, so undo the premultiply. If the source is premultiplied we
// must fix that as well.
fs << " backdrop.rgb /= backdrop.a;" << endl;
if (!(aConfig.mFeatures & ENABLE_NO_PREMUL_ALPHA)) {
fs << " color.rgb /= color.a;" << endl;
}
fs << " vec3 blended = blend(backdrop.rgb, color.rgb);" << endl;
fs << " color.rgb = (1.0 - backdrop.a) * color.rgb + backdrop.a * blended.rgb;" << endl;
fs << " color.rgb *= color.a;" << endl;
fs << " return color;" << endl;
fs << "}" << endl;
}
ShaderProgramOGL::ShaderProgramOGL(GLContext* aGL, const ProgramProfileOGL& aProfile)
: mGL(aGL)
, mProgram(0)

View File

@ -39,7 +39,7 @@ enum ShaderFeatures {
ENABLE_COLOR_MATRIX=0x400,
ENABLE_MASK_2D=0x800,
ENABLE_MASK_3D=0x1000,
ENABLE_PREMULTIPLY=0x2000,
ENABLE_NO_PREMUL_ALPHA=0x2000,
ENABLE_DEAA=0x4000
};
@ -65,6 +65,7 @@ public:
BlackTexture,
WhiteTexture,
MaskTexture,
BackdropTexture,
RenderColor,
TexCoordMultiplier,
CbCrTexCoordMultiplier,
@ -207,7 +208,9 @@ class ShaderConfigOGL
{
public:
ShaderConfigOGL() :
mFeatures(0) {}
mFeatures(0),
mCompositionOp(gfx::CompositionOp::OP_OVER)
{}
void SetRenderColor(bool aEnabled);
void SetTextureTarget(GLenum aTarget);
@ -221,11 +224,14 @@ public:
void SetBlur(bool aEnabled);
void SetMask2D(bool aEnabled);
void SetMask3D(bool aEnabled);
void SetPremultiply(bool aEnabled);
void SetDEAA(bool aEnabled);
void SetCompositionOp(gfx::CompositionOp aOp);
void SetNoPremultipliedAlpha();
bool operator< (const ShaderConfigOGL& other) const {
return mFeatures < other.mFeatures;
return mFeatures < other.mFeatures ||
(mFeatures == other.mFeatures &&
(int)mCompositionOp < (int)other.mCompositionOp);
}
public:
@ -237,6 +243,7 @@ public:
}
int mFeatures;
gfx::CompositionOp mCompositionOp;
};
static inline ShaderConfigOGL
@ -278,6 +285,9 @@ struct ProgramProfileOGL
ProgramProfileOGL() :
mTextureCount(0)
{}
private:
static void BuildMixBlender(const ShaderConfigOGL& aConfig, std::ostringstream& fs);
};
@ -433,6 +443,10 @@ public:
SetUniform(KnownUniform::MaskTexture, aUnit);
}
void SetBackdropTextureUnit(GLint aUnit) {
SetUniform(KnownUniform::BackdropTexture, aUnit);
}
void SetRenderColor(const gfx::Color& aColor) {
SetUniform(KnownUniform::RenderColor, aColor);
}

View File

@ -0,0 +1,21 @@
<!DOCTYPE HTML>
<head>
<style>
.parent {
width: 200px;
height: 200px;
isolation: isolate;
}
.child {
width: 200px;
height: 200px;
background: #7775b6;
}
</style>
</head>
<body>
<div class="parent">
<div class="child">
</div>
</div>
</body>

View File

@ -0,0 +1,25 @@
<!DOCTYPE HTML>
<head>
<style>
.parent {
width: 200px;
height: 200px;
isolation: isolate;
background: #5856a2;
}
.child {
width: 200px;
height: 200px;
mix-blend-mode: soft-light;
opacity: 0.5;
background: white;
will-change: opacity;
}
</style>
</head>
<body>
<div class="parent">
<div class="child">
</div>
</div>
</body>

View File

@ -43,7 +43,7 @@ fuzzy(64,37) pref(layout.css.mix-blend-mode.enabled,true) == mix-blend-mode-9520
pref(layout.css.mix-blend-mode.enabled,true) pref(layout.css.filters.enabled,true) == mix-blend-mode-and-filter.html mix-blend-mode-and-filter-ref.html
pref(layout.css.mix-blend-mode.enabled,true) pref(layout.css.filters.enabled,true) == mix-blend-mode-and-filter.svg mix-blend-mode-and-filter-ref.svg
pref(layout.css.mix-blend-mode.enabled,true) fuzzy-if(d2d,1,14400) == mix-blend-mode-child-of-blended-has-opacity.html mix-blend-mode-child-of-blended-has-opacity-ref.html
fuzzy(1,14400) pref(layout.css.mix-blend-mode.enabled,true) == mix-blend-mode-child-of-blended-has-opacity.html mix-blend-mode-child-of-blended-has-opacity-ref.html
pref(layout.css.mix-blend-mode.enabled,true) == mix-blend-mode-nested-976533.html mix-blend-mode-nested-976533-ref.html
pref(layout.css.mix-blend-mode.enabled,true) == mix-blend-mode-culling-1207041.html mix-blend-mode-culling-1207041-ref.html
@ -86,9 +86,11 @@ pref(layout.css.background-blend-mode.enabled,true) == background-blending-backg
pref(layout.css.background-blend-mode.enabled,true) == background-blending-background-attachement-fixed-scroll.html background-blending-background-attachement-fixed-scroll-ref.html
pref(layout.css.background-blend-mode.enabled,true) == background-blend-mode-body-image.html background-blend-mode-body-image-ref.html
fuzzy-if(Android,4,768) pref(layout.css.background-blend-mode.enabled,true) == background-blend-mode-body-transparent-image.html background-blend-mode-body-transparent-image-ref.html
fuzzy-if(Android,4,768) fuzzy-if(gtkWidget,1,132) pref(layout.css.background-blend-mode.enabled,true) == background-blend-mode-body-transparent-image.html background-blend-mode-body-transparent-image-ref.html
pref(layout.css.background-blend-mode.enabled,true) == background-blending-moz-element.html background-blending-moz-element-ref.html
fuzzy(1,40000) pref(layout.css.background-blend-mode.enabled,true) == mix-blend-mode-soft-light.html mix-blend-mode-soft-light-ref.html
# Test plan 4.4.2 element with isolation:isolate creates an isolated group for blended children
pref(layout.css.isolation.enabled,true) == blend-isolation.html blend-isolation-ref.html