SVGMaskFrame.cpp (6614B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 // Main header first: 8 #include "SVGMaskFrame.h" 9 10 // Keep others in (case-insensitive) order: 11 #include "AutoReferenceChainGuard.h" 12 #include "gfx2DGlue.h" 13 #include "gfxContext.h" 14 #include "mozilla/PresShell.h" 15 #include "mozilla/RefPtr.h" 16 #include "mozilla/SVGObserverUtils.h" 17 #include "mozilla/SVGUtils.h" 18 #include "mozilla/dom/SVGMaskElement.h" 19 #include "mozilla/dom/SVGUnitTypesBinding.h" 20 #include "mozilla/gfx/2D.h" 21 22 using namespace mozilla::dom; 23 using namespace mozilla::dom::SVGUnitTypes_Binding; 24 using namespace mozilla::gfx; 25 using namespace mozilla::image; 26 27 nsIFrame* NS_NewSVGMaskFrame(mozilla::PresShell* aPresShell, 28 mozilla::ComputedStyle* aStyle) { 29 return new (aPresShell) 30 mozilla::SVGMaskFrame(aStyle, aPresShell->GetPresContext()); 31 } 32 33 namespace mozilla { 34 35 NS_IMPL_FRAMEARENA_HELPERS(SVGMaskFrame) 36 37 already_AddRefed<SourceSurface> SVGMaskFrame::GetMaskForMaskedFrame( 38 MaskParams& aParams) { 39 // Make sure we break reference loops and over long reference chains: 40 static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain; 41 AutoReferenceChainGuard refChainGuard(this, &mInUse, &sRefChainLengthCounter); 42 if (MOZ_UNLIKELY(!refChainGuard.Reference())) { 43 // Break reference chain 44 return nullptr; 45 } 46 47 gfxRect maskArea = GetMaskArea(aParams.maskedFrame); 48 if (maskArea.IsEmpty()) { 49 return nullptr; 50 } 51 // Get the clip extents in device space: 52 // Minimizing the mask surface extents (using both the current clip extents 53 // and maskArea) is important for performance. 54 // 55 gfxRect maskSurfaceRectDouble = aParams.toUserSpace.TransformBounds(maskArea); 56 Rect maskSurfaceRect = ToRect(maskSurfaceRectDouble); 57 maskSurfaceRect.RoundOut(); 58 59 StyleMaskType maskType; 60 if (aParams.maskMode == StyleMaskMode::MatchSource) { 61 maskType = StyleSVGReset()->mMaskType; 62 } else { 63 maskType = aParams.maskMode == StyleMaskMode::Luminance 64 ? StyleMaskType::Luminance 65 : StyleMaskType::Alpha; 66 } 67 68 RefPtr<DrawTarget> maskDT; 69 if (maskType == StyleMaskType::Luminance) { 70 maskDT = aParams.dt->CreateClippedDrawTarget(maskSurfaceRect, 71 SurfaceFormat::B8G8R8A8); 72 } else { 73 maskDT = 74 aParams.dt->CreateClippedDrawTarget(maskSurfaceRect, SurfaceFormat::A8); 75 } 76 77 if (!maskDT || !maskDT->IsValid()) { 78 return nullptr; 79 } 80 81 gfxContext tmpCtx(maskDT, /* aPreserveTransform */ true); 82 83 mMatrixForChildren = 84 GetMaskTransform(aParams.maskedFrame) * aParams.toUserSpace; 85 86 for (auto* kid : mFrames) { 87 gfxMatrix m = mMatrixForChildren; 88 89 // The CTM of each frame referencing us can be different 90 ISVGDisplayableFrame* SVGFrame = do_QueryFrame(kid); 91 if (SVGFrame) { 92 SVGFrame->NotifySVGChanged( 93 ISVGDisplayableFrame::ChangeFlag::TransformChanged); 94 m = SVGUtils::GetTransformMatrixInUserSpace(kid) * m; 95 } 96 97 SVGUtils::PaintFrameWithEffects(kid, tmpCtx, m, aParams.imgParams); 98 } 99 100 RefPtr<SourceSurface> surface; 101 if (maskType == StyleMaskType::Luminance) { 102 auto luminanceType = LuminanceType::LUMINANCE; 103 if (StyleSVG()->mColorInterpolation == StyleColorInterpolation::Linearrgb) { 104 luminanceType = LuminanceType::LINEARRGB; 105 } 106 107 RefPtr<SourceSurface> maskSnapshot = 108 maskDT->IntoLuminanceSource(luminanceType, aParams.opacity); 109 if (!maskSnapshot) { 110 return nullptr; 111 } 112 surface = std::move(maskSnapshot); 113 } else { 114 maskDT->FillRect(maskSurfaceRect, 115 ColorPattern(DeviceColor::MaskWhite(aParams.opacity)), 116 DrawOptions(1, CompositionOp::OP_IN)); 117 RefPtr<SourceSurface> maskSnapshot = maskDT->Snapshot(); 118 if (!maskSnapshot) { 119 return nullptr; 120 } 121 surface = std::move(maskSnapshot); 122 } 123 124 return surface.forget(); 125 } 126 127 gfxRect SVGMaskFrame::GetMaskArea(nsIFrame* aMaskedFrame) { 128 SVGMaskElement* maskElem = static_cast<SVGMaskElement*>(GetContent()); 129 130 uint16_t units = 131 maskElem->mEnumAttributes[SVGMaskElement::MASKUNITS].GetAnimValue(); 132 gfxRect bbox; 133 if (units == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { 134 bbox = 135 SVGUtils::GetBBox(aMaskedFrame, SVGUtils::eUseFrameBoundsForOuterSVG | 136 SVGUtils::eBBoxIncludeFillGeometry); 137 } 138 139 // Bounds in the user space of aMaskedFrame 140 gfxRect maskArea = SVGUtils::GetRelativeRect( 141 units, &maskElem->mLengthAttributes[SVGMaskElement::ATTR_X], bbox, 142 aMaskedFrame); 143 144 return maskArea; 145 } 146 147 nsresult SVGMaskFrame::AttributeChanged(int32_t aNameSpaceID, 148 nsAtom* aAttribute, 149 AttrModType aModType) { 150 if (aNameSpaceID == kNameSpaceID_None && 151 (aAttribute == nsGkAtoms::x || aAttribute == nsGkAtoms::y || 152 aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height || 153 aAttribute == nsGkAtoms::maskUnits || 154 aAttribute == nsGkAtoms::maskContentUnits)) { 155 SVGObserverUtils::InvalidateRenderingObservers(this); 156 } 157 158 return SVGContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, 159 aModType); 160 } 161 162 #ifdef DEBUG 163 void SVGMaskFrame::Init(nsIContent* aContent, nsContainerFrame* aParent, 164 nsIFrame* aPrevInFlow) { 165 NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::mask), 166 "Content is not an SVG mask"); 167 168 SVGContainerFrame::Init(aContent, aParent, aPrevInFlow); 169 } 170 #endif /* DEBUG */ 171 172 gfxMatrix SVGMaskFrame::GetCanvasTM() { return mMatrixForChildren; } 173 174 gfxMatrix SVGMaskFrame::GetMaskTransform(nsIFrame* aMaskedFrame) { 175 SVGMaskElement* content = static_cast<SVGMaskElement*>(GetContent()); 176 177 SVGAnimatedEnumeration* maskContentUnits = 178 &content->mEnumAttributes[SVGMaskElement::MASKCONTENTUNITS]; 179 180 uint32_t flags = SVGUtils::eBBoxIncludeFillGeometry | 181 (aMaskedFrame->StyleBorder()->mBoxDecorationBreak == 182 StyleBoxDecorationBreak::Clone 183 ? SVGUtils::eIncludeOnlyCurrentFrameForNonSVGElement 184 : 0); 185 186 return SVGUtils::AdjustMatrixForUnits(gfxMatrix(), maskContentUnits, 187 aMaskedFrame, flags); 188 } 189 190 } // namespace mozilla