/src/mozilla-central/gfx/thebes/gfxDrawable.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
20 | | gfxSurfaceDrawable::gfxSurfaceDrawable(SourceSurface* aSurface, |
21 | | const IntSize aSize, |
22 | | const gfxMatrix aTransform) |
23 | | : gfxDrawable(aSize) |
24 | | , mSourceSurface(aSurface) |
25 | | , mTransform(aTransform) |
26 | 0 | { |
27 | 0 | if (!mSourceSurface) { |
28 | 0 | gfxWarning() << "Creating gfxSurfaceDrawable with null SourceSurface"; |
29 | 0 | } |
30 | 0 | } |
31 | | |
32 | | bool |
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 | 0 | { |
42 | 0 | if (!mSourceSurface) { |
43 | 0 | return true; |
44 | 0 | } |
45 | 0 | |
46 | 0 | // When drawing with CLAMP we can expand the sampling rect to the nearest pixel |
47 | 0 | // without changing the result. |
48 | 0 | IntRect intRect = IntRect::RoundOut(aSamplingRect.X(), aSamplingRect.Y(), |
49 | 0 | aSamplingRect.Width(), aSamplingRect.Height()); |
50 | 0 |
|
51 | 0 | IntSize size = mSourceSurface->GetSize(); |
52 | 0 | if (!IntRect(IntPoint(), size).Contains(intRect)) { |
53 | 0 | return false; |
54 | 0 | } |
55 | 0 | |
56 | 0 | DrawInternal(aDrawTarget, aOp, aAntialiasMode, aFillRect, intRect, |
57 | 0 | ExtendMode::CLAMP, aSamplingFilter, aOpacity, gfxMatrix()); |
58 | 0 | return true; |
59 | 0 | } |
60 | | |
61 | | bool |
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 | 0 | { |
70 | 0 | if (!mSourceSurface) { |
71 | 0 | return true; |
72 | 0 | } |
73 | 0 | |
74 | 0 | DrawInternal(aContext->GetDrawTarget(), aContext->CurrentOp(), |
75 | 0 | aContext->CurrentAntialiasMode(), aFillRect, IntRect(), |
76 | 0 | aExtendMode, aSamplingFilter, aOpacity, aTransform); |
77 | 0 | return true; |
78 | 0 | } |
79 | | |
80 | | void |
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 | 0 | { |
91 | 0 | Matrix patternTransform = ToMatrix(aTransform * mTransform); |
92 | 0 | patternTransform.Invert(); |
93 | 0 |
|
94 | 0 | SurfacePattern pattern(mSourceSurface, aExtendMode, |
95 | 0 | patternTransform, aSamplingFilter, aSamplingRect); |
96 | 0 |
|
97 | 0 | Rect fillRect = ToRect(aFillRect); |
98 | 0 |
|
99 | 0 | if (aOp == CompositionOp::OP_SOURCE && aOpacity == 1.0) { |
100 | 0 | // Emulate cairo operator source which is bound by mask! |
101 | 0 | aDrawTarget->ClearRect(fillRect); |
102 | 0 | aDrawTarget->FillRect(fillRect, pattern); |
103 | 0 | } else { |
104 | 0 | aDrawTarget->FillRect(fillRect, pattern, |
105 | 0 | DrawOptions(aOpacity, aOp, aAntialiasMode)); |
106 | 0 | } |
107 | 0 | } |
108 | | |
109 | | gfxCallbackDrawable::gfxCallbackDrawable(gfxDrawingCallback* aCallback, |
110 | | const IntSize aSize) |
111 | | : gfxDrawable(aSize) |
112 | | , mCallback(aCallback) |
113 | 0 | { |
114 | 0 | } |
115 | | |
116 | | already_AddRefed<gfxSurfaceDrawable> |
117 | | gfxCallbackDrawable::MakeSurfaceDrawable(gfxContext *aContext, const SamplingFilter aSamplingFilter) |
118 | 0 | { |
119 | 0 | SurfaceFormat format = |
120 | 0 | gfxPlatform::GetPlatform()->Optimal2DFormatForContent(gfxContentType::COLOR_ALPHA); |
121 | 0 | RefPtr<DrawTarget> dt = |
122 | 0 | aContext->GetDrawTarget()->CreateSimilarDrawTarget(mSize, format); |
123 | 0 |
|
124 | 0 | if (!dt || !dt->IsValid()) |
125 | 0 | return nullptr; |
126 | 0 | |
127 | 0 | RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt); |
128 | 0 | MOZ_ASSERT(ctx); // already checked for target above |
129 | 0 | Draw(ctx, gfxRect(0, 0, mSize.width, mSize.height), ExtendMode::CLAMP, |
130 | 0 | aSamplingFilter); |
131 | 0 |
|
132 | 0 | RefPtr<SourceSurface> surface = dt->Snapshot(); |
133 | 0 | if (surface) { |
134 | 0 | RefPtr<gfxSurfaceDrawable> drawable = new gfxSurfaceDrawable(surface, mSize); |
135 | 0 | return drawable.forget(); |
136 | 0 | } |
137 | 0 | return nullptr; |
138 | 0 | } |
139 | | |
140 | | static bool |
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 |
154 | | gfxCallbackDrawable::Draw(gfxContext* aContext, |
155 | | const gfxRect& aFillRect, |
156 | | ExtendMode aExtendMode, |
157 | | const SamplingFilter aSamplingFilter, |
158 | | gfxFloat aOpacity, |
159 | | const gfxMatrix& aTransform) |
160 | 0 | { |
161 | 0 | if ((IsRepeatingExtendMode(aExtendMode) || aOpacity != 1.0 || aContext->CurrentOp() != CompositionOp::OP_OVER) && |
162 | 0 | !mSurfaceDrawable) { |
163 | 0 | mSurfaceDrawable = MakeSurfaceDrawable(aContext, aSamplingFilter); |
164 | 0 | } |
165 | 0 |
|
166 | 0 | if (mSurfaceDrawable) |
167 | 0 | return mSurfaceDrawable->Draw(aContext, aFillRect, aExtendMode, |
168 | 0 | aSamplingFilter, |
169 | 0 | aOpacity, aTransform); |
170 | 0 | |
171 | 0 | if (mCallback) |
172 | 0 | return (*mCallback)(aContext, aFillRect, aSamplingFilter, aTransform); |
173 | 0 | |
174 | 0 | return false; |
175 | 0 | } |
176 | | |
177 | | gfxPatternDrawable::gfxPatternDrawable(gfxPattern* aPattern, |
178 | | const IntSize aSize) |
179 | | : gfxDrawable(aSize) |
180 | | , mPattern(aPattern) |
181 | 0 | { |
182 | 0 | } |
183 | | |
184 | 0 | gfxPatternDrawable::~gfxPatternDrawable() = default; |
185 | | |
186 | | class DrawingCallbackFromDrawable : public gfxDrawingCallback { |
187 | | public: |
188 | | explicit DrawingCallbackFromDrawable(gfxDrawable* aDrawable) |
189 | 0 | : mDrawable(aDrawable) { |
190 | 0 | NS_ASSERTION(aDrawable, "aDrawable is null!"); |
191 | 0 | } |
192 | | |
193 | 0 | ~DrawingCallbackFromDrawable() override = default; |
194 | | |
195 | | bool operator()(gfxContext* aContext, |
196 | | const gfxRect& aFillRect, |
197 | | const SamplingFilter aSamplingFilter, |
198 | | const gfxMatrix& aTransform = gfxMatrix()) override |
199 | 0 | { |
200 | 0 | return mDrawable->Draw(aContext, aFillRect, ExtendMode::CLAMP, |
201 | 0 | aSamplingFilter, 1.0, |
202 | 0 | aTransform); |
203 | 0 | } |
204 | | private: |
205 | | RefPtr<gfxDrawable> mDrawable; |
206 | | }; |
207 | | |
208 | | already_AddRefed<gfxCallbackDrawable> |
209 | | gfxPatternDrawable::MakeCallbackDrawable() |
210 | 0 | { |
211 | 0 | RefPtr<gfxDrawingCallback> callback = |
212 | 0 | new DrawingCallbackFromDrawable(this); |
213 | 0 | RefPtr<gfxCallbackDrawable> callbackDrawable = |
214 | 0 | new gfxCallbackDrawable(callback, mSize); |
215 | 0 | return callbackDrawable.forget(); |
216 | 0 | } |
217 | | |
218 | | bool |
219 | | gfxPatternDrawable::Draw(gfxContext* aContext, |
220 | | const gfxRect& aFillRect, |
221 | | ExtendMode aExtendMode, |
222 | | const SamplingFilter aSamplingFilter, |
223 | | gfxFloat aOpacity, |
224 | | const gfxMatrix& aTransform) |
225 | 0 | { |
226 | 0 | DrawTarget& aDrawTarget = *aContext->GetDrawTarget(); |
227 | 0 |
|
228 | 0 | if (!mPattern) |
229 | 0 | return false; |
230 | 0 | |
231 | 0 | if (IsRepeatingExtendMode(aExtendMode)) { |
232 | 0 | // We can't use mPattern directly: We want our repeated tiles to have |
233 | 0 | // the size mSize, which might not be the case in mPattern. |
234 | 0 | // So we need to draw mPattern into a surface of size mSize, create |
235 | 0 | // a pattern from the surface and draw that pattern. |
236 | 0 | // gfxCallbackDrawable and gfxSurfaceDrawable already know how to do |
237 | 0 | // those things, so we use them here. Drawing mPattern into the surface |
238 | 0 | // will happen through this Draw() method with aRepeat = false. |
239 | 0 | RefPtr<gfxCallbackDrawable> callbackDrawable = MakeCallbackDrawable(); |
240 | 0 | return callbackDrawable->Draw(aContext, aFillRect, aExtendMode, |
241 | 0 | aSamplingFilter, |
242 | 0 | aOpacity, aTransform); |
243 | 0 | } |
244 | 0 | |
245 | 0 | gfxMatrix oldMatrix = mPattern->GetMatrix(); |
246 | 0 | mPattern->SetMatrix(aTransform * oldMatrix); |
247 | 0 | DrawOptions drawOptions(aOpacity); |
248 | 0 | aDrawTarget.FillRect(ToRect(aFillRect), |
249 | 0 | *mPattern->GetPattern(&aDrawTarget), drawOptions); |
250 | 0 | mPattern->SetMatrix(oldMatrix); |
251 | 0 | return true; |
252 | 0 | } |