--- a/layout/svg/nsSVGMaskFrame.cpp
+++ b/layout/svg/nsSVGMaskFrame.cpp
@@ -54,16 +54,63 @@ 147, 149, 151, 152, 154, 156, 157, 159,
161, 163, 164, 166, 168, 170, 171, 173,
175, 177, 179, 181, 183, 184, 186, 188,
190, 192, 194, 196, 198, 200, 202, 204,
206, 208, 210, 212, 214, 216, 218, 220,
222, 224, 226, 229, 231, 233, 235, 237,
239, 242, 244, 246, 248, 250, 253, 255
};
+struct CompositionNone {
+ static uint8_t Compose(uint8_t aSourceAlpha, uint8_t aDestAlpah,
+ float aOpacity)
+ {
+ // No blend.
+ return aSourceAlpha * aOpacity;
+ }
+};
+
+struct CompositionOver {
+ static uint8_t Compose(uint8_t aSourceAlpha, uint8_t aDestAlpah,
+ float aOpacity)
+ {
+ // αo = αs + αb x (1 – αs)
+ return aSourceAlpha + aDestAlpah * ((255 - aSourceAlpha) / 255.0) * aOpacity;
+ }
+};
+
+struct CompositionSubtract {
+ static uint8_t Compose(uint8_t aSourceAlpha, uint8_t aDestAlpah,
+ float aOpacity)
+ {
+ // αo = αs x (1 – αb)
+ return aSourceAlpha * ((255 - aDestAlpah) / 255.0) * aOpacity;
+ }
+};
+
+struct CompositionIntersect {
+ static uint8_t Compose(uint8_t aSourceAlpha, uint8_t aDestAlpah,
+ float aOpacity)
+ {
+ // αo = αs x αb
+ return aSourceAlpha * (aSourceAlpha / 255.0) * aOpacity;
+ }
+};
+
+struct CompositionExclude {
+ static uint8_t Compose(uint8_t aSourceAlpha, uint8_t aDestAlpah,
+ float aOpacity)
+ {
+ // αo = αs x (1 - αb) + αb x (1 – αs)
+ return (aSourceAlpha * ((255 - aDestAlpah) / 255.0) +
+ aDestAlpah * ((255 - aSourceAlpha) / 255.0))* aOpacity;
+ }
+};
+
+template<typename CompositionT>
static void
ComputesRGBLuminanceMask(const uint8_t *aSourceData,
int32_t aSourceStride,
uint8_t *aDestData,
int32_t aDestStride,
const IntSize &aSize,
float aOpacity)
{
@@ -84,31 +131,98 @@ ComputesRGBLuminanceMask(const uint8_t *
int32_t destOffset = aDestStride - aSize.width;
uint8_t *destPixel = aDestData;
for (int32_t y = 0; y < aSize.height; y++) {
for (int32_t x = 0; x < aSize.width; x++) {
uint8_t a = sourcePixel[GFX_ARGB32_OFFSET_A];
if (a) {
- *destPixel = (redFactor * sourcePixel[GFX_ARGB32_OFFSET_R] +
- greenFactor * sourcePixel[GFX_ARGB32_OFFSET_G] +
- blueFactor * sourcePixel[GFX_ARGB32_OFFSET_B]) >> 8;
+ uint8_t sourceAlpha =
+ (redFactor * sourcePixel[GFX_ARGB32_OFFSET_R] +
+ greenFactor * sourcePixel[GFX_ARGB32_OFFSET_G] +
+ blueFactor * sourcePixel[GFX_ARGB32_OFFSET_B]) >> 8;
+ *destPixel = CompositionT::Compose(sourceAlpha, *destPixel, aOpacity);
} else {
- *destPixel = 0;
+ *destPixel = CompositionT::Compose(0, *destPixel, aOpacity);
}
sourcePixel += 4;
destPixel++;
}
sourcePixel += sourceOffset;
destPixel += destOffset;
}
}
static void
+ComputesRGBLuminanceMask(const uint8_t *aSourceData,
+ int32_t aSourceStride,
+ uint8_t *aDestData,
+ int32_t aDestStride,
+ const IntSize &aSize,
+ float aOpacity,
+ CompositionOp compositionOp,
+ bool aBlendTarget)
+{
+ if (aBlendTarget) {
+ switch (compositionOp) {
+ case CompositionOp::OP_OVER:
+ ComputesRGBLuminanceMask<CompositionOver>(aSourceData,
+ aSourceStride,
+ aDestData,
+ aDestStride,
+ aSize,
+ aOpacity);
+ break;
+ case CompositionOp::OP_OUT:
+ ComputesRGBLuminanceMask<CompositionSubtract>(aSourceData,
+ aSourceStride,
+ aDestData,
+ aDestStride,
+ aSize,
+ aOpacity);
+ break;
+ case CompositionOp::OP_IN:
+ ComputesRGBLuminanceMask<CompositionIntersect>(aSourceData,
+ aSourceStride,
+ aDestData,
+ aDestStride,
+ aSize,
+ aOpacity);
+ break;
+ case CompositionOp::OP_XOR:
+ ComputesRGBLuminanceMask<CompositionExclude>(aSourceData,
+ aSourceStride,
+ aDestData,
+ aDestStride,
+ aSize,
+ aOpacity);
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("unexpected composition type!");
+ ComputesRGBLuminanceMask<CompositionOver>(aSourceData,
+ aSourceStride,
+ aDestData,
+ aDestStride,
+ aSize,
+ aOpacity);
+ break;
+ }
+ } else {
+ ComputesRGBLuminanceMask<CompositionNone>(aSourceData,
+ aSourceStride,
+ aDestData,
+ aDestStride,
+ aSize,
+ aOpacity);
+ }
+}
+
+template<typename CompositionT>
+static void
ComputeLinearRGBLuminanceMask(const uint8_t *aSourceData,
int32_t aSourceStride,
uint8_t *aDestData,
int32_t aDestStride,
const IntSize &aSize,
float aOpacity)
{
int32_t redFactor = 55 * aOpacity; // 255 * 0.2125 * opacity
@@ -122,78 +236,207 @@ ComputeLinearRGBLuminanceMask(const uint
for (int32_t y = 0; y < aSize.height; y++) {
for (int32_t x = 0; x < aSize.width; x++) {
uint8_t a = sourcePixel[GFX_ARGB32_OFFSET_A];
// unpremultiply
if (a) {
if (a == 255) {
/* sRGB -> linearRGB -> intensity */
- *destPixel =
+ uint8_t sourceAlpha =
static_cast<uint8_t>
((gsRGBToLinearRGBMap[sourcePixel[GFX_ARGB32_OFFSET_R]] *
redFactor +
gsRGBToLinearRGBMap[sourcePixel[GFX_ARGB32_OFFSET_G]] *
greenFactor +
gsRGBToLinearRGBMap[sourcePixel[GFX_ARGB32_OFFSET_B]] *
blueFactor) >> 8);
+ *destPixel = CompositionT::Compose(sourceAlpha, *destPixel, aOpacity);
} else {
uint8_t tempPixel[4];
tempPixel[GFX_ARGB32_OFFSET_B] =
(255 * sourcePixel[GFX_ARGB32_OFFSET_B]) / a;
tempPixel[GFX_ARGB32_OFFSET_G] =
(255 * sourcePixel[GFX_ARGB32_OFFSET_G]) / a;
tempPixel[GFX_ARGB32_OFFSET_R] =
(255 * sourcePixel[GFX_ARGB32_OFFSET_R]) / a;
/* sRGB -> linearRGB -> intensity */
- *destPixel =
+ uint8_t sourceAlpha =
static_cast<uint8_t>
(((gsRGBToLinearRGBMap[tempPixel[GFX_ARGB32_OFFSET_R]] *
redFactor +
gsRGBToLinearRGBMap[tempPixel[GFX_ARGB32_OFFSET_G]] *
greenFactor +
gsRGBToLinearRGBMap[tempPixel[GFX_ARGB32_OFFSET_B]] *
blueFactor) >> 8) * (a / 255.0f));
+ *destPixel = CompositionT::Compose(sourceAlpha, *destPixel, aOpacity);
}
} else {
- *destPixel = 0;
+ *destPixel = CompositionT::Compose(0, *destPixel, aOpacity);
}
sourcePixel += 4;
destPixel++;
}
sourcePixel += sourceOffset;
destPixel += destOffset;
}
}
static void
+ComputeLinearRGBLuminanceMask(const uint8_t *aSourceData,
+ int32_t aSourceStride,
+ uint8_t *aDestData,
+ int32_t aDestStride,
+ const IntSize &aSize,
+ float aOpacity,
+ CompositionOp compositionOp,
+ bool aBlendTarget)
+{
+ if (aBlendTarget) {
+ switch (compositionOp) {
+ case CompositionOp::OP_OVER:
+ ComputeLinearRGBLuminanceMask<CompositionOver>(aSourceData,
+ aSourceStride,
+ aDestData,
+ aDestStride,
+ aSize,
+ aOpacity);
+ break;
+ case CompositionOp::OP_OUT:
+ ComputeLinearRGBLuminanceMask<CompositionSubtract>(aSourceData,
+ aSourceStride,
+ aDestData,
+ aDestStride,
+ aSize,
+ aOpacity);
+ break;
+ case CompositionOp::OP_IN:
+ ComputeLinearRGBLuminanceMask<CompositionIntersect>(aSourceData,
+ aSourceStride,
+ aDestData,
+ aDestStride,
+ aSize,
+ aOpacity);
+ break;
+ case CompositionOp::OP_XOR:
+ ComputeLinearRGBLuminanceMask<CompositionExclude>(aSourceData,
+ aSourceStride,
+ aDestData,
+ aDestStride,
+ aSize,
+ aOpacity);
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("unexpected composition type!");
+ ComputeLinearRGBLuminanceMask<CompositionOver>(aSourceData,
+ aSourceStride,
+ aDestData,
+ aDestStride,
+ aSize,
+ aOpacity);
+ break;
+ }
+ } else {
+ ComputeLinearRGBLuminanceMask<CompositionNone>(aSourceData,
+ aSourceStride,
+ aDestData,
+ aDestStride,
+ aSize,
+ aOpacity);
+ }
+}
+
+template<typename CompositionT>
+static void
ComputeAlphaMask(const uint8_t *aSourceData,
int32_t aSourceStride,
uint8_t *aDestData,
int32_t aDestStride,
const IntSize &aSize,
float aOpacity)
{
int32_t sourceOffset = aSourceStride - 4 * aSize.width;
const uint8_t *sourcePixel = aSourceData;
int32_t destOffset = aDestStride - aSize.width;
uint8_t *destPixel = aDestData;
for (int32_t y = 0; y < aSize.height; y++) {
for (int32_t x = 0; x < aSize.width; x++) {
- *destPixel = sourcePixel[GFX_ARGB32_OFFSET_A] * aOpacity;
+ *destPixel = CompositionT::Compose(sourcePixel[GFX_ARGB32_OFFSET_A],
+ *destPixel,
+ aOpacity);
sourcePixel += 4;
destPixel++;
}
sourcePixel += sourceOffset;
destPixel += destOffset;
}
}
+static void
+ComputeAlphaMask(const uint8_t *aSourceData,
+ int32_t aSourceStride,
+ uint8_t *aDestData,
+ int32_t aDestStride,
+ const IntSize &aSize,
+ float aOpacity,
+ CompositionOp compositionOp,
+ bool aBlendTarget)
+{
+ if (aBlendTarget) {
+ switch (compositionOp) {
+ case CompositionOp::OP_OVER:
+ ComputeAlphaMask<CompositionOver>(aSourceData,
+ aSourceStride,
+ aDestData,
+ aDestStride,
+ aSize,
+ aOpacity);
+ break;
+ case CompositionOp::OP_OUT:
+ ComputeAlphaMask<CompositionSubtract>(aSourceData,
+ aSourceStride,
+ aDestData,
+ aDestStride,
+ aSize,
+ aOpacity);
+ break;
+ case CompositionOp::OP_IN:
+ ComputeAlphaMask<CompositionIntersect>(aSourceData,
+ aSourceStride,
+ aDestData,
+ aDestStride,
+ aSize,
+ aOpacity);
+ break;
+ case CompositionOp::OP_XOR:
+ ComputeAlphaMask<CompositionExclude>(aSourceData,
+ aSourceStride,
+ aDestData,
+ aDestStride,
+ aSize,
+ aOpacity);
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("unexpected composition type!");
+ ComputeAlphaMask<CompositionOver>(aSourceData,
+ aSourceStride,
+ aDestData,
+ aDestStride,
+ aSize,
+ aOpacity);
+ break;
+ }
+ } else {
+ ComputeAlphaMask<CompositionNone>(aSourceData, aSourceStride, aDestData,
+ aDestStride, aSize, aOpacity);
+ }
+}
+
//----------------------------------------------------------------------
// Implementation
nsIFrame*
NS_NewSVGMaskFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
{
return new (aPresShell) nsSVGMaskFrame(aContext);
}
@@ -277,19 +520,20 @@ nsSVGMaskFrame::GetMaskForMaskedFrame(co
}
RefPtr<DataSourceSurface> maskSurface = maskSnapshot->GetDataSurface();
DataSourceSurface::MappedSurface map;
if (!maskSurface->Map(DataSourceSurface::MapType::READ, &map)) {
return nullptr;
}
// Create alpha channel mask for output
- RefPtr<DrawTarget> destMaskDT =
- Factory::CreateDrawTarget(BackendType::CAIRO, maskSurfaceSize,
- SurfaceFormat::A8);
+ RefPtr<DrawTarget> destMaskDT = aParams.maskDT
+ ? RefPtr<DrawTarget>(aParams.maskDT).forget()
+ : Factory::CreateDrawTarget(BackendType::CAIRO, maskSurfaceSize,
+ SurfaceFormat::A8);
if (!destMaskDT) {
return nullptr;
}
RefPtr<SourceSurface> destMaskSnapshot = destMaskDT->Snapshot();
if (!destMaskSnapshot) {
return nullptr;
}
RefPtr<DataSourceSurface> destMaskSurface = destMaskSnapshot->GetDataSurface();
@@ -304,28 +548,30 @@ nsSVGMaskFrame::GetMaskForMaskedFrame(co
} else {
maskType = aParams.maskOp == NS_STYLE_MASK_MODE_LUMINANCE ?
NS_STYLE_MASK_TYPE_LUMINANCE : NS_STYLE_MASK_TYPE_ALPHA;
}
if (maskType == NS_STYLE_MASK_TYPE_LUMINANCE) {
if (StyleSVG()->mColorInterpolation ==
NS_STYLE_COLOR_INTERPOLATION_LINEARRGB) {
- ComputeLinearRGBLuminanceMask(map.mData, map.mStride,
- destMap.mData, destMap.mStride,
- maskSurfaceSize, aParams.opacity);
+ ComputeLinearRGBLuminanceMask(map.mData, map.mStride, destMap.mData,
+ destMap.mStride, maskSurfaceSize,
+ aParams.opacity, aParams.compositionOp,
+ aParams.maskDT);
} else {
- ComputesRGBLuminanceMask(map.mData, map.mStride,
- destMap.mData, destMap.mStride,
- maskSurfaceSize, aParams.opacity);
+ ComputesRGBLuminanceMask(map.mData, map.mStride, destMap.mData,
+ destMap.mStride, maskSurfaceSize,
+ aParams.opacity, aParams.compositionOp,
+ aParams.maskDT);
}
} else {
- ComputeAlphaMask(map.mData, map.mStride,
- destMap.mData, destMap.mStride,
- maskSurfaceSize, aParams.opacity);
+ ComputeAlphaMask(map.mData, map.mStride, destMap.mData, destMap.mStride,
+ maskSurfaceSize, aParams.opacity, aParams.compositionOp,
+ aParams.maskDT);
}
maskSurface->Unmap();
destMaskSurface->Unmap();
// Moz2D transforms in the opposite direction to Thebes
if (!maskSurfaceMatrix.Invert()) {
return nullptr;