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