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