1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "gfxDrawable.h"
7 #include "gfxASurface.h"
8 #include "gfxContext.h"
9 #include "gfxPlatform.h"
10 #include "gfx2DGlue.h"
11 #ifdef MOZ_X11
12 #include "cairo.h"
13 #include "gfxXlibSurface.h"
14 #endif
15 #include "mozilla/gfx/Logging.h"
16 
17 using namespace mozilla;
18 using namespace mozilla::gfx;
19 
gfxSurfaceDrawable(SourceSurface * aSurface,const IntSize aSize,const gfxMatrix aTransform)20 gfxSurfaceDrawable::gfxSurfaceDrawable(SourceSurface* aSurface,
21                                        const IntSize aSize,
22                                        const gfxMatrix aTransform)
23  : gfxDrawable(aSize)
24  , mSourceSurface(aSurface)
25  , mTransform(aTransform)
26 {
27   if (!mSourceSurface) {
28     gfxWarning() << "Creating gfxSurfaceDrawable with null SourceSurface";
29   }
30 }
31 
32 bool
DrawWithSamplingRect(DrawTarget * aDrawTarget,CompositionOp aOp,AntialiasMode aAntialiasMode,const gfxRect & aFillRect,const gfxRect & aSamplingRect,ExtendMode aExtendMode,const SamplingFilter aSamplingFilter,gfxFloat aOpacity)33 gfxSurfaceDrawable::DrawWithSamplingRect(DrawTarget* aDrawTarget,
34                                          CompositionOp aOp,
35                                          AntialiasMode aAntialiasMode,
36                                          const gfxRect& aFillRect,
37                                          const gfxRect& aSamplingRect,
38                                          ExtendMode aExtendMode,
39                                          const SamplingFilter aSamplingFilter,
40                                          gfxFloat aOpacity)
41 {
42   if (!mSourceSurface) {
43     return true;
44   }
45 
46   // When drawing with CLAMP we can expand the sampling rect to the nearest pixel
47   // without changing the result.
48   IntRect intRect = IntRect::RoundOut(aSamplingRect.x, aSamplingRect.y,
49                                       aSamplingRect.width, aSamplingRect.height);
50 
51   IntSize size = mSourceSurface->GetSize();
52   if (!IntRect(IntPoint(), size).Contains(intRect)) {
53     return false;
54   }
55 
56   DrawInternal(aDrawTarget, aOp, aAntialiasMode, aFillRect, intRect,
57                ExtendMode::CLAMP, aSamplingFilter, aOpacity, gfxMatrix());
58   return true;
59 }
60 
61 bool
Draw(gfxContext * aContext,const gfxRect & aFillRect,ExtendMode aExtendMode,const SamplingFilter aSamplingFilter,gfxFloat aOpacity,const gfxMatrix & aTransform)62 gfxSurfaceDrawable::Draw(gfxContext* aContext,
63                          const gfxRect& aFillRect,
64                          ExtendMode aExtendMode,
65                          const SamplingFilter aSamplingFilter,
66                          gfxFloat aOpacity,
67                          const gfxMatrix& aTransform)
68 
69 {
70   if (!mSourceSurface) {
71     return true;
72   }
73 
74   DrawInternal(aContext->GetDrawTarget(), aContext->CurrentOp(),
75                aContext->CurrentAntialiasMode(), aFillRect, IntRect(),
76                aExtendMode, aSamplingFilter, aOpacity, aTransform);
77   return true;
78 }
79 
80 void
DrawInternal(DrawTarget * aDrawTarget,CompositionOp aOp,AntialiasMode aAntialiasMode,const gfxRect & aFillRect,const IntRect & aSamplingRect,ExtendMode aExtendMode,const SamplingFilter aSamplingFilter,gfxFloat aOpacity,const gfxMatrix & aTransform)81 gfxSurfaceDrawable::DrawInternal(DrawTarget* aDrawTarget,
82                                  CompositionOp aOp,
83                                  AntialiasMode aAntialiasMode,
84                                  const gfxRect& aFillRect,
85                                  const IntRect& aSamplingRect,
86                                  ExtendMode aExtendMode,
87                                  const SamplingFilter aSamplingFilter,
88                                  gfxFloat aOpacity,
89                                  const gfxMatrix& aTransform)
90 {
91     Matrix patternTransform = ToMatrix(aTransform * mTransform);
92     patternTransform.Invert();
93 
94     SurfacePattern pattern(mSourceSurface, aExtendMode,
95                            patternTransform, aSamplingFilter, aSamplingRect);
96 
97     Rect fillRect = ToRect(aFillRect);
98 
99     if (aOp == CompositionOp::OP_SOURCE && aOpacity == 1.0) {
100         // Emulate cairo operator source which is bound by mask!
101         aDrawTarget->ClearRect(fillRect);
102         aDrawTarget->FillRect(fillRect, pattern);
103     } else {
104         aDrawTarget->FillRect(fillRect, pattern,
105                               DrawOptions(aOpacity, aOp, aAntialiasMode));
106     }
107 }
108 
gfxCallbackDrawable(gfxDrawingCallback * aCallback,const IntSize aSize)109 gfxCallbackDrawable::gfxCallbackDrawable(gfxDrawingCallback* aCallback,
110                                          const IntSize aSize)
111  : gfxDrawable(aSize)
112  , mCallback(aCallback)
113 {
114 }
115 
116 already_AddRefed<gfxSurfaceDrawable>
MakeSurfaceDrawable(const SamplingFilter aSamplingFilter)117 gfxCallbackDrawable::MakeSurfaceDrawable(const SamplingFilter aSamplingFilter)
118 {
119     SurfaceFormat format =
120         gfxPlatform::GetPlatform()->Optimal2DFormatForContent(gfxContentType::COLOR_ALPHA);
121     RefPtr<DrawTarget> dt =
122         gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(mSize,
123                                                                      format);
124     if (!dt || !dt->IsValid())
125         return nullptr;
126 
127     RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
128     MOZ_ASSERT(ctx); // already checked for target above
129     Draw(ctx, gfxRect(0, 0, mSize.width, mSize.height), ExtendMode::CLAMP,
130          aSamplingFilter);
131 
132     RefPtr<SourceSurface> surface = dt->Snapshot();
133     if (surface) {
134         RefPtr<gfxSurfaceDrawable> drawable = new gfxSurfaceDrawable(surface, mSize);
135         return drawable.forget();
136     }
137     return nullptr;
138 }
139 
140 static bool
IsRepeatingExtendMode(ExtendMode aExtendMode)141 IsRepeatingExtendMode(ExtendMode aExtendMode)
142 {
143   switch (aExtendMode) {
144   case ExtendMode::REPEAT:
145   case ExtendMode::REPEAT_X:
146   case ExtendMode::REPEAT_Y:
147     return true;
148   default:
149     return false;
150   }
151 }
152 
153 bool
Draw(gfxContext * aContext,const gfxRect & aFillRect,ExtendMode aExtendMode,const SamplingFilter aSamplingFilter,gfxFloat aOpacity,const gfxMatrix & aTransform)154 gfxCallbackDrawable::Draw(gfxContext* aContext,
155                           const gfxRect& aFillRect,
156                           ExtendMode aExtendMode,
157                           const SamplingFilter aSamplingFilter,
158                           gfxFloat aOpacity,
159                           const gfxMatrix& aTransform)
160 {
161     if ((IsRepeatingExtendMode(aExtendMode) || aOpacity != 1.0 || aContext->CurrentOp() != CompositionOp::OP_OVER) &&
162         !mSurfaceDrawable) {
163         mSurfaceDrawable = MakeSurfaceDrawable(aSamplingFilter);
164     }
165 
166     if (mSurfaceDrawable)
167         return mSurfaceDrawable->Draw(aContext, aFillRect, aExtendMode,
168                                       aSamplingFilter,
169                                       aOpacity, aTransform);
170 
171     if (mCallback)
172         return (*mCallback)(aContext, aFillRect, aSamplingFilter, aTransform);
173 
174     return false;
175 }
176 
gfxPatternDrawable(gfxPattern * aPattern,const IntSize aSize)177 gfxPatternDrawable::gfxPatternDrawable(gfxPattern* aPattern,
178                                        const IntSize aSize)
179  : gfxDrawable(aSize)
180  , mPattern(aPattern)
181 {
182 }
183 
~gfxPatternDrawable()184 gfxPatternDrawable::~gfxPatternDrawable()
185 {
186 }
187 
188 class DrawingCallbackFromDrawable : public gfxDrawingCallback {
189 public:
DrawingCallbackFromDrawable(gfxDrawable * aDrawable)190     explicit DrawingCallbackFromDrawable(gfxDrawable* aDrawable)
191      : mDrawable(aDrawable) {
192         NS_ASSERTION(aDrawable, "aDrawable is null!");
193     }
194 
~DrawingCallbackFromDrawable()195     virtual ~DrawingCallbackFromDrawable() {}
196 
operator ()(gfxContext * aContext,const gfxRect & aFillRect,const SamplingFilter aSamplingFilter,const gfxMatrix & aTransform=gfxMatrix ())197     virtual bool operator()(gfxContext* aContext,
198                               const gfxRect& aFillRect,
199                               const SamplingFilter aSamplingFilter,
200                               const gfxMatrix& aTransform = gfxMatrix())
201     {
202         return mDrawable->Draw(aContext, aFillRect, ExtendMode::CLAMP,
203                                aSamplingFilter, 1.0,
204                                aTransform);
205     }
206 private:
207     RefPtr<gfxDrawable> mDrawable;
208 };
209 
210 already_AddRefed<gfxCallbackDrawable>
MakeCallbackDrawable()211 gfxPatternDrawable::MakeCallbackDrawable()
212 {
213     RefPtr<gfxDrawingCallback> callback =
214         new DrawingCallbackFromDrawable(this);
215     RefPtr<gfxCallbackDrawable> callbackDrawable =
216         new gfxCallbackDrawable(callback, mSize);
217     return callbackDrawable.forget();
218 }
219 
220 bool
Draw(gfxContext * aContext,const gfxRect & aFillRect,ExtendMode aExtendMode,const SamplingFilter aSamplingFilter,gfxFloat aOpacity,const gfxMatrix & aTransform)221 gfxPatternDrawable::Draw(gfxContext* aContext,
222                          const gfxRect& aFillRect,
223                          ExtendMode aExtendMode,
224                          const SamplingFilter aSamplingFilter,
225                          gfxFloat aOpacity,
226                          const gfxMatrix& aTransform)
227 {
228     DrawTarget& aDrawTarget = *aContext->GetDrawTarget();
229 
230     if (!mPattern)
231         return false;
232 
233     if (IsRepeatingExtendMode(aExtendMode)) {
234         // We can't use mPattern directly: We want our repeated tiles to have
235         // the size mSize, which might not be the case in mPattern.
236         // So we need to draw mPattern into a surface of size mSize, create
237         // a pattern from the surface and draw that pattern.
238         // gfxCallbackDrawable and gfxSurfaceDrawable already know how to do
239         // those things, so we use them here. Drawing mPattern into the surface
240         // will happen through this Draw() method with aRepeat = false.
241         RefPtr<gfxCallbackDrawable> callbackDrawable = MakeCallbackDrawable();
242         return callbackDrawable->Draw(aContext, aFillRect, aExtendMode,
243                                       aSamplingFilter,
244                                       aOpacity, aTransform);
245     }
246 
247     gfxMatrix oldMatrix = mPattern->GetMatrix();
248     mPattern->SetMatrix(aTransform * oldMatrix);
249     DrawOptions drawOptions(aOpacity);
250     aDrawTarget.FillRect(ToRect(aFillRect),
251                          *mPattern->GetPattern(&aDrawTarget), drawOptions);
252     mPattern->SetMatrix(oldMatrix);
253     return true;
254 }
255