1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
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), mSourceSurface(aSurface), mTransform(aTransform) {
24 if (!mSourceSurface) {
25 gfxWarning() << "Creating gfxSurfaceDrawable with null SourceSurface";
26 }
27 }
28
DrawWithSamplingRect(DrawTarget * aDrawTarget,CompositionOp aOp,AntialiasMode aAntialiasMode,const gfxRect & aFillRect,const gfxRect & aSamplingRect,ExtendMode aExtendMode,const SamplingFilter aSamplingFilter,gfxFloat aOpacity)29 bool gfxSurfaceDrawable::DrawWithSamplingRect(
30 DrawTarget* aDrawTarget, CompositionOp aOp, AntialiasMode aAntialiasMode,
31 const gfxRect& aFillRect, const gfxRect& aSamplingRect,
32 ExtendMode aExtendMode, const SamplingFilter aSamplingFilter,
33 gfxFloat aOpacity) {
34 if (!mSourceSurface) {
35 return true;
36 }
37
38 // When drawing with CLAMP we can expand the sampling rect to the nearest
39 // pixel without changing the result.
40 IntRect intRect =
41 IntRect::RoundOut(aSamplingRect.X(), aSamplingRect.Y(),
42 aSamplingRect.Width(), aSamplingRect.Height());
43
44 IntSize size = mSourceSurface->GetSize();
45 if (!IntRect(IntPoint(), size).Contains(intRect)) {
46 return false;
47 }
48
49 DrawInternal(aDrawTarget, aOp, aAntialiasMode, aFillRect, intRect,
50 ExtendMode::CLAMP, aSamplingFilter, aOpacity, gfxMatrix());
51 return true;
52 }
53
Draw(gfxContext * aContext,const gfxRect & aFillRect,ExtendMode aExtendMode,const SamplingFilter aSamplingFilter,gfxFloat aOpacity,const gfxMatrix & aTransform)54 bool gfxSurfaceDrawable::Draw(gfxContext* aContext, const gfxRect& aFillRect,
55 ExtendMode aExtendMode,
56 const SamplingFilter aSamplingFilter,
57 gfxFloat aOpacity, const gfxMatrix& aTransform)
58
59 {
60 if (!mSourceSurface) {
61 return true;
62 }
63
64 DrawInternal(aContext->GetDrawTarget(), aContext->CurrentOp(),
65 aContext->CurrentAntialiasMode(), aFillRect, IntRect(),
66 aExtendMode, aSamplingFilter, aOpacity, aTransform);
67 return true;
68 }
69
DrawInternal(DrawTarget * aDrawTarget,CompositionOp aOp,AntialiasMode aAntialiasMode,const gfxRect & aFillRect,const IntRect & aSamplingRect,ExtendMode aExtendMode,const SamplingFilter aSamplingFilter,gfxFloat aOpacity,const gfxMatrix & aTransform)70 void gfxSurfaceDrawable::DrawInternal(
71 DrawTarget* aDrawTarget, CompositionOp aOp, AntialiasMode aAntialiasMode,
72 const gfxRect& aFillRect, const IntRect& aSamplingRect,
73 ExtendMode aExtendMode, const SamplingFilter aSamplingFilter,
74 gfxFloat aOpacity, const gfxMatrix& aTransform) {
75 Matrix patternTransform = ToMatrix(aTransform * mTransform);
76 patternTransform.Invert();
77
78 SurfacePattern pattern(mSourceSurface, aExtendMode, patternTransform,
79 aSamplingFilter, aSamplingRect);
80
81 Rect fillRect = ToRect(aFillRect);
82
83 if (aOp == CompositionOp::OP_SOURCE && aOpacity == 1.0) {
84 // Emulate cairo operator source which is bound by mask!
85 aDrawTarget->ClearRect(fillRect);
86 aDrawTarget->FillRect(fillRect, pattern);
87 } else {
88 aDrawTarget->FillRect(fillRect, pattern,
89 DrawOptions(aOpacity, aOp, aAntialiasMode));
90 }
91 }
92
gfxCallbackDrawable(gfxDrawingCallback * aCallback,const IntSize aSize)93 gfxCallbackDrawable::gfxCallbackDrawable(gfxDrawingCallback* aCallback,
94 const IntSize aSize)
95 : gfxDrawable(aSize), mCallback(aCallback) {}
96
MakeSurfaceDrawable(gfxContext * aContext,const SamplingFilter aSamplingFilter)97 already_AddRefed<gfxSurfaceDrawable> gfxCallbackDrawable::MakeSurfaceDrawable(
98 gfxContext* aContext, const SamplingFilter aSamplingFilter) {
99 SurfaceFormat format = gfxPlatform::GetPlatform()->Optimal2DFormatForContent(
100 gfxContentType::COLOR_ALPHA);
101 if (!aContext->GetDrawTarget()->CanCreateSimilarDrawTarget(mSize, format)) {
102 return nullptr;
103 }
104 RefPtr<DrawTarget> dt =
105 aContext->GetDrawTarget()->CreateSimilarDrawTarget(mSize, format);
106
107 if (!dt || !dt->IsValid()) return nullptr;
108
109 RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
110 MOZ_ASSERT(ctx); // already checked for target above
111 Draw(ctx, gfxRect(0, 0, mSize.width, mSize.height), ExtendMode::CLAMP,
112 aSamplingFilter);
113
114 RefPtr<SourceSurface> surface = dt->Snapshot();
115 if (surface) {
116 RefPtr<gfxSurfaceDrawable> drawable =
117 new gfxSurfaceDrawable(surface, mSize);
118 return drawable.forget();
119 }
120 return nullptr;
121 }
122
IsRepeatingExtendMode(ExtendMode aExtendMode)123 static bool IsRepeatingExtendMode(ExtendMode aExtendMode) {
124 switch (aExtendMode) {
125 case ExtendMode::REPEAT:
126 case ExtendMode::REPEAT_X:
127 case ExtendMode::REPEAT_Y:
128 return true;
129 default:
130 return false;
131 }
132 }
133
Draw(gfxContext * aContext,const gfxRect & aFillRect,ExtendMode aExtendMode,const SamplingFilter aSamplingFilter,gfxFloat aOpacity,const gfxMatrix & aTransform)134 bool gfxCallbackDrawable::Draw(gfxContext* aContext, const gfxRect& aFillRect,
135 ExtendMode aExtendMode,
136 const SamplingFilter aSamplingFilter,
137 gfxFloat aOpacity, const gfxMatrix& aTransform) {
138 if ((IsRepeatingExtendMode(aExtendMode) || aOpacity != 1.0 ||
139 aContext->CurrentOp() != CompositionOp::OP_OVER) &&
140 !mSurfaceDrawable) {
141 mSurfaceDrawable = MakeSurfaceDrawable(aContext, aSamplingFilter);
142 }
143
144 if (mSurfaceDrawable)
145 return mSurfaceDrawable->Draw(aContext, aFillRect, aExtendMode,
146 aSamplingFilter, aOpacity, aTransform);
147
148 if (mCallback)
149 return (*mCallback)(aContext, aFillRect, aSamplingFilter, aTransform);
150
151 return false;
152 }
153
gfxPatternDrawable(gfxPattern * aPattern,const IntSize aSize)154 gfxPatternDrawable::gfxPatternDrawable(gfxPattern* aPattern,
155 const IntSize aSize)
156 : gfxDrawable(aSize), mPattern(aPattern) {}
157
158 gfxPatternDrawable::~gfxPatternDrawable() = default;
159
160 class DrawingCallbackFromDrawable : public gfxDrawingCallback {
161 public:
DrawingCallbackFromDrawable(gfxDrawable * aDrawable)162 explicit DrawingCallbackFromDrawable(gfxDrawable* aDrawable)
163 : mDrawable(aDrawable) {
164 NS_ASSERTION(aDrawable, "aDrawable is null!");
165 }
166
167 virtual ~DrawingCallbackFromDrawable() = default;
168
operator ()(gfxContext * aContext,const gfxRect & aFillRect,const SamplingFilter aSamplingFilter,const gfxMatrix & aTransform=gfxMatrix ())169 bool operator()(gfxContext* aContext, const gfxRect& aFillRect,
170 const SamplingFilter aSamplingFilter,
171 const gfxMatrix& aTransform = gfxMatrix()) override {
172 return mDrawable->Draw(aContext, aFillRect, ExtendMode::CLAMP,
173 aSamplingFilter, 1.0, aTransform);
174 }
175
176 private:
177 RefPtr<gfxDrawable> mDrawable;
178 };
179
180 already_AddRefed<gfxCallbackDrawable>
MakeCallbackDrawable()181 gfxPatternDrawable::MakeCallbackDrawable() {
182 RefPtr<gfxDrawingCallback> callback = new DrawingCallbackFromDrawable(this);
183 RefPtr<gfxCallbackDrawable> callbackDrawable =
184 new gfxCallbackDrawable(callback, mSize);
185 return callbackDrawable.forget();
186 }
187
Draw(gfxContext * aContext,const gfxRect & aFillRect,ExtendMode aExtendMode,const SamplingFilter aSamplingFilter,gfxFloat aOpacity,const gfxMatrix & aTransform)188 bool gfxPatternDrawable::Draw(gfxContext* aContext, const gfxRect& aFillRect,
189 ExtendMode aExtendMode,
190 const SamplingFilter aSamplingFilter,
191 gfxFloat aOpacity, const gfxMatrix& aTransform) {
192 DrawTarget& aDrawTarget = *aContext->GetDrawTarget();
193
194 if (!mPattern) return false;
195
196 if (IsRepeatingExtendMode(aExtendMode)) {
197 // We can't use mPattern directly: We want our repeated tiles to have
198 // the size mSize, which might not be the case in mPattern.
199 // So we need to draw mPattern into a surface of size mSize, create
200 // a pattern from the surface and draw that pattern.
201 // gfxCallbackDrawable and gfxSurfaceDrawable already know how to do
202 // those things, so we use them here. Drawing mPattern into the surface
203 // will happen through this Draw() method with aRepeat = false.
204 RefPtr<gfxCallbackDrawable> callbackDrawable = MakeCallbackDrawable();
205 return callbackDrawable->Draw(aContext, aFillRect, aExtendMode,
206 aSamplingFilter, aOpacity, aTransform);
207 }
208
209 gfxMatrix oldMatrix = mPattern->GetMatrix();
210 mPattern->SetMatrix(aTransform * oldMatrix);
211 DrawOptions drawOptions(aOpacity);
212 aDrawTarget.FillRect(ToRect(aFillRect), *mPattern->GetPattern(&aDrawTarget),
213 drawOptions);
214 mPattern->SetMatrix(oldMatrix);
215 return true;
216 }
217