Coverage Report

Created: 2018-09-25 14:53

/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
}