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