1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "SVGContextPaint.h" 6 7 #include "gfxContext.h" 8 #include "mozilla/gfx/2D.h" 9 #include "nsIDocument.h" 10 #include "nsSVGPaintServerFrame.h" 11 #include "nsSVGEffects.h" 12 #include "nsSVGPaintServerFrame.h" 13 14 using namespace mozilla::gfx; 15 16 namespace mozilla { 17 18 /** 19 * Stores in |aTargetPaint| information on how to reconstruct the current 20 * fill or stroke pattern. Will also set the paint opacity to transparent if 21 * the paint is set to "none". 22 * @param aOuterContextPaint pattern information from the outer text context 23 * @param aTargetPaint where to store the current pattern information 24 * @param aFillOrStroke member pointer to the paint we are setting up 25 * @param aProperty the frame property descriptor of the fill or stroke paint 26 * server frame 27 */ 28 static void 29 SetupInheritablePaint(const DrawTarget* aDrawTarget, 30 const gfxMatrix& aContextMatrix, 31 nsIFrame* aFrame, 32 float& aOpacity, 33 SVGContextPaint* aOuterContextPaint, 34 SVGContextPaintImpl::Paint& aTargetPaint, 35 nsStyleSVGPaint nsStyleSVG::*aFillOrStroke, 36 nsSVGEffects::PaintingPropertyDescriptor aProperty) 37 { 38 const nsStyleSVG *style = aFrame->StyleSVG(); 39 nsSVGPaintServerFrame *ps = 40 nsSVGEffects::GetPaintServer(aFrame, aFillOrStroke, aProperty); 41 42 if (ps) { 43 RefPtr<gfxPattern> pattern = 44 ps->GetPaintServerPattern(aFrame, aDrawTarget, aContextMatrix, 45 aFillOrStroke, aOpacity); 46 if (pattern) { 47 aTargetPaint.SetPaintServer(aFrame, aContextMatrix, ps); 48 return; 49 } 50 } 51 if (aOuterContextPaint) { 52 RefPtr<gfxPattern> pattern; 53 switch ((style->*aFillOrStroke).Type()) { 54 case eStyleSVGPaintType_ContextFill: 55 pattern = aOuterContextPaint->GetFillPattern(aDrawTarget, aOpacity, 56 aContextMatrix); 57 break; 58 case eStyleSVGPaintType_ContextStroke: 59 pattern = aOuterContextPaint->GetStrokePattern(aDrawTarget, aOpacity, 60 aContextMatrix); 61 break; 62 default: 63 ; 64 } 65 if (pattern) { 66 aTargetPaint.SetContextPaint(aOuterContextPaint, (style->*aFillOrStroke).Type()); 67 return; 68 } 69 } 70 nscolor color = 71 nsSVGUtils::GetFallbackOrPaintColor(aFrame->StyleContext(), aFillOrStroke); 72 aTargetPaint.SetColor(color); 73 } 74 75 DrawMode 76 SVGContextPaintImpl::Init(const DrawTarget* aDrawTarget, auth_password(struct ssh * ssh,const char * password)77 const gfxMatrix& aContextMatrix, 78 nsIFrame* aFrame, 79 SVGContextPaint* aOuterContextPaint) 80 { 81 DrawMode toDraw = DrawMode(0); 82 83 const nsStyleSVG *style = aFrame->StyleSVG(); 84 85 // fill: 86 if (style->mFill.Type() == eStyleSVGPaintType_None) { 87 SetFillOpacity(0.0f); 88 } else { 89 float opacity = nsSVGUtils::GetOpacity(style->FillOpacitySource(), 90 style->mFillOpacity, 91 aOuterContextPaint); 92 93 SetupInheritablePaint(aDrawTarget, aContextMatrix, aFrame, 94 opacity, aOuterContextPaint, 95 mFillPaint, &nsStyleSVG::mFill, 96 nsSVGEffects::FillProperty()); 97 98 SetFillOpacity(opacity); 99 100 toDraw |= DrawMode::GLYPH_FILL; 101 } 102 103 // stroke: 104 if (style->mStroke.Type() == eStyleSVGPaintType_None) { 105 SetStrokeOpacity(0.0f); 106 } else { 107 float opacity = nsSVGUtils::GetOpacity(style->StrokeOpacitySource(), 108 style->mStrokeOpacity, 109 aOuterContextPaint); 110 111 SetupInheritablePaint(aDrawTarget, aContextMatrix, aFrame, 112 opacity, aOuterContextPaint, 113 mStrokePaint, &nsStyleSVG::mStroke, 114 nsSVGEffects::StrokeProperty()); 115 116 SetStrokeOpacity(opacity); 117 118 toDraw |= DrawMode::GLYPH_STROKE; 119 } 120 121 return toDraw; 122 } 123 124 void 125 SVGContextPaint::InitStrokeGeometry(gfxContext* aContext, 126 float devUnitsPerSVGUnit) 127 { 128 mStrokeWidth = aContext->CurrentLineWidth() / devUnitsPerSVGUnit; 129 aContext->CurrentDash(mDashes, &mDashOffset); 130 for (uint32_t i = 0; i < mDashes.Length(); i++) { 131 mDashes[i] /= devUnitsPerSVGUnit; 132 } warn_expiry(Authctxt * authctxt,auth_session_t * as)133 mDashOffset /= devUnitsPerSVGUnit; 134 } 135 136 /* static */ SVGContextPaint* 137 SVGContextPaint::GetContextPaint(nsIContent* aContent) 138 { 139 nsIDocument* ownerDoc = aContent->OwnerDoc(); 140 141 if (!ownerDoc->IsBeingUsedAsImage()) { 142 return nullptr; 143 } 144 145 return static_cast<SVGContextPaint*>( 146 ownerDoc->GetProperty(nsGkAtoms::svgContextPaint)); 147 } 148 149 already_AddRefed<gfxPattern> 150 SVGContextPaintImpl::GetFillPattern(const DrawTarget* aDrawTarget, 151 float aOpacity, 152 const gfxMatrix& aCTM) 153 { 154 return mFillPaint.GetPattern(aDrawTarget, aOpacity, &nsStyleSVG::mFill, aCTM); 155 } 156 157 already_AddRefed<gfxPattern> 158 SVGContextPaintImpl::GetStrokePattern(const DrawTarget* aDrawTarget, 159 float aOpacity, 160 const gfxMatrix& aCTM) 161 { 162 return mStrokePaint.GetPattern(aDrawTarget, aOpacity, &nsStyleSVG::mStroke, aCTM); 163 } 164 165 already_AddRefed<gfxPattern> 166 SVGContextPaintImpl::Paint::GetPattern(const DrawTarget* aDrawTarget, sys_auth_passwd(struct ssh * ssh,const char * password)167 float aOpacity, 168 nsStyleSVGPaint nsStyleSVG::*aFillOrStroke, 169 const gfxMatrix& aCTM) 170 { 171 RefPtr<gfxPattern> pattern; 172 if (mPatternCache.Get(aOpacity, getter_AddRefs(pattern))) { 173 // Set the pattern matrix just in case it was messed with by a previous 174 // caller. We should get the same matrix each time a pattern is constructed 175 // so this should be fine. 176 pattern->SetMatrix(aCTM * mPatternMatrix); 177 return pattern.forget(); 178 } 179 180 switch (mPaintType) { 181 case eStyleSVGPaintType_None: 182 pattern = new gfxPattern(Color()); 183 mPatternMatrix = gfxMatrix(); 184 break; 185 case eStyleSVGPaintType_Color: { 186 Color color = Color::FromABGR(mPaintDefinition.mColor); 187 color.a *= aOpacity; 188 pattern = new gfxPattern(color); 189 mPatternMatrix = gfxMatrix(); 190 break; 191 } 192 case eStyleSVGPaintType_Server: 193 pattern = mPaintDefinition.mPaintServerFrame->GetPaintServerPattern(mFrame, 194 aDrawTarget, 195 mContextMatrix, 196 aFillOrStroke, 197 aOpacity); 198 { 199 // m maps original-user-space to pattern space 200 gfxMatrix m = pattern->GetMatrix(); 201 gfxMatrix deviceToOriginalUserSpace = mContextMatrix; 202 if (!deviceToOriginalUserSpace.Invert()) { 203 return nullptr; 204 } 205 // mPatternMatrix maps device space to pattern space via original user space 206 mPatternMatrix = deviceToOriginalUserSpace * m; 207 } 208 pattern->SetMatrix(aCTM * mPatternMatrix); 209 break; 210 case eStyleSVGPaintType_ContextFill: 211 pattern = mPaintDefinition.mContextPaint->GetFillPattern(aDrawTarget, 212 aOpacity, aCTM); 213 // Don't cache this. mContextPaint will have cached it anyway. If we 214 // cache it, we'll have to compute mPatternMatrix, which is annoying. 215 return pattern.forget(); 216 case eStyleSVGPaintType_ContextStroke: 217 pattern = mPaintDefinition.mContextPaint->GetStrokePattern(aDrawTarget, 218 aOpacity, aCTM); 219 // Don't cache this. mContextPaint will have cached it anyway. If we 220 // cache it, we'll have to compute mPatternMatrix, which is annoying. 221 return pattern.forget(); 222 default: 223 MOZ_ASSERT(false, "invalid paint type"); 224 return nullptr; 225 } 226 227 mPatternCache.Put(aOpacity, pattern); 228 return pattern.forget(); 229 } 230 231 AutoSetRestoreSVGContextPaint::AutoSetRestoreSVGContextPaint( 232 SVGContextPaint* aContextPaint, 233 nsIDocument* aSVGDocument) 234 : mSVGDocument(aSVGDocument) 235 , mOuterContextPaint(aSVGDocument->GetProperty(nsGkAtoms::svgContextPaint)) 236 { 237 // The way that we supply context paint is to temporarily set the context 238 // paint on the owner document of the SVG that we're painting while it's 239 // being painted. 240 241 MOZ_ASSERT(aContextPaint); 242 MOZ_ASSERT(aSVGDocument->IsBeingUsedAsImage(), 243 "SVGContextPaint::GetContextPaint assumes this"); 244 245 if (mOuterContextPaint) { 246 mSVGDocument->UnsetProperty(nsGkAtoms::svgContextPaint); 247 } 248 249 DebugOnly<nsresult> res = 250 mSVGDocument->SetProperty(nsGkAtoms::svgContextPaint, aContextPaint); 251 252 NS_WARNING_ASSERTION(NS_SUCCEEDED(res), "Failed to set context paint"); 253 } 254 255 AutoSetRestoreSVGContextPaint::~AutoSetRestoreSVGContextPaint() 256 { 257 mSVGDocument->UnsetProperty(nsGkAtoms::svgContextPaint); 258 if (mOuterContextPaint) { 259 DebugOnly<nsresult> res = 260 mSVGDocument->SetProperty(nsGkAtoms::svgContextPaint, mOuterContextPaint); 261 262 NS_WARNING_ASSERTION(NS_SUCCEEDED(res), "Failed to restore context paint"); 263 } 264 } 265 266 } // namespace mozilla 267