Bug 1732219 - Add API for fetching the preview image. r=geckoview-reviewers,agi,mconley
[gecko.git] / gfx / thebes / gfxDrawable.cpp
blob7b26f2de9c7f2b9009512b6d653f5ff38d0a8470
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/. */
6 #include "gfxDrawable.h"
7 #include "gfxContext.h"
8 #include "gfxPlatform.h"
9 #include "gfx2DGlue.h"
10 #ifdef MOZ_X11
11 # include "cairo.h"
12 # include "gfxXlibSurface.h"
13 #endif
14 #include "mozilla/gfx/Logging.h"
16 using namespace mozilla;
17 using namespace mozilla::gfx;
19 gfxSurfaceDrawable::gfxSurfaceDrawable(SourceSurface* aSurface,
20 const IntSize aSize,
21 const gfxMatrix aTransform)
22 : gfxDrawable(aSize), mSourceSurface(aSurface), mTransform(aTransform) {
23 if (!mSourceSurface) {
24 gfxWarning() << "Creating gfxSurfaceDrawable with null SourceSurface";
28 bool gfxSurfaceDrawable::DrawWithSamplingRect(
29 DrawTarget* aDrawTarget, CompositionOp aOp, AntialiasMode aAntialiasMode,
30 const gfxRect& aFillRect, const gfxRect& aSamplingRect,
31 ExtendMode aExtendMode, const SamplingFilter aSamplingFilter,
32 gfxFloat aOpacity) {
33 if (!mSourceSurface) {
34 return true;
37 // When drawing with CLAMP we can expand the sampling rect to the nearest
38 // pixel without changing the result.
39 IntRect intRect =
40 IntRect::RoundOut(aSamplingRect.X(), aSamplingRect.Y(),
41 aSamplingRect.Width(), aSamplingRect.Height());
43 IntSize size = mSourceSurface->GetSize();
44 if (!IntRect(IntPoint(), size).Contains(intRect)) {
45 return false;
48 DrawInternal(aDrawTarget, aOp, aAntialiasMode, aFillRect, intRect,
49 ExtendMode::CLAMP, aSamplingFilter, aOpacity, gfxMatrix());
50 return true;
53 bool gfxSurfaceDrawable::Draw(gfxContext* aContext, const gfxRect& aFillRect,
54 ExtendMode aExtendMode,
55 const SamplingFilter aSamplingFilter,
56 gfxFloat aOpacity, const gfxMatrix& aTransform)
59 if (!mSourceSurface) {
60 return true;
63 DrawInternal(aContext->GetDrawTarget(), aContext->CurrentOp(),
64 aContext->CurrentAntialiasMode(), aFillRect, IntRect(),
65 aExtendMode, aSamplingFilter, aOpacity, aTransform);
66 return true;
69 void gfxSurfaceDrawable::DrawInternal(
70 DrawTarget* aDrawTarget, CompositionOp aOp, AntialiasMode aAntialiasMode,
71 const gfxRect& aFillRect, const IntRect& aSamplingRect,
72 ExtendMode aExtendMode, const SamplingFilter aSamplingFilter,
73 gfxFloat aOpacity, const gfxMatrix& aTransform) {
74 Matrix patternTransform = ToMatrix(aTransform * mTransform);
75 patternTransform.Invert();
77 SurfacePattern pattern(mSourceSurface, aExtendMode, patternTransform,
78 aSamplingFilter, aSamplingRect);
80 Rect fillRect = ToRect(aFillRect);
82 if (aOp == CompositionOp::OP_SOURCE && aOpacity == 1.0) {
83 // Emulate cairo operator source which is bound by mask!
84 aDrawTarget->ClearRect(fillRect);
85 aDrawTarget->FillRect(fillRect, pattern);
86 } else {
87 aDrawTarget->FillRect(fillRect, pattern,
88 DrawOptions(aOpacity, aOp, aAntialiasMode));
92 gfxCallbackDrawable::gfxCallbackDrawable(gfxDrawingCallback* aCallback,
93 const IntSize aSize)
94 : gfxDrawable(aSize), mCallback(aCallback) {}
96 already_AddRefed<gfxSurfaceDrawable> gfxCallbackDrawable::MakeSurfaceDrawable(
97 gfxContext* aContext, const SamplingFilter aSamplingFilter) {
98 SurfaceFormat format = gfxPlatform::GetPlatform()->Optimal2DFormatForContent(
99 gfxContentType::COLOR_ALPHA);
100 if (!aContext->GetDrawTarget()->CanCreateSimilarDrawTarget(mSize, format)) {
101 return nullptr;
103 RefPtr<DrawTarget> dt =
104 aContext->GetDrawTarget()->CreateSimilarDrawTarget(mSize, format);
106 if (!dt || !dt->IsValid()) return nullptr;
108 RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
109 MOZ_ASSERT(ctx); // already checked for target above
110 Draw(ctx, gfxRect(0, 0, mSize.width, mSize.height), ExtendMode::CLAMP,
111 aSamplingFilter);
113 RefPtr<SourceSurface> surface = dt->Snapshot();
114 if (surface) {
115 RefPtr<gfxSurfaceDrawable> drawable =
116 new gfxSurfaceDrawable(surface, mSize);
117 return drawable.forget();
119 return nullptr;
122 static bool IsRepeatingExtendMode(ExtendMode aExtendMode) {
123 switch (aExtendMode) {
124 case ExtendMode::REPEAT:
125 case ExtendMode::REPEAT_X:
126 case ExtendMode::REPEAT_Y:
127 return true;
128 default:
129 return false;
133 bool gfxCallbackDrawable::Draw(gfxContext* aContext, const gfxRect& aFillRect,
134 ExtendMode aExtendMode,
135 const SamplingFilter aSamplingFilter,
136 gfxFloat aOpacity, const gfxMatrix& aTransform) {
137 if ((IsRepeatingExtendMode(aExtendMode) || aOpacity != 1.0 ||
138 aContext->CurrentOp() != CompositionOp::OP_OVER) &&
139 !mSurfaceDrawable) {
140 mSurfaceDrawable = MakeSurfaceDrawable(aContext, aSamplingFilter);
143 if (mSurfaceDrawable)
144 return mSurfaceDrawable->Draw(aContext, aFillRect, aExtendMode,
145 aSamplingFilter, aOpacity, aTransform);
147 if (mCallback)
148 return (*mCallback)(aContext, aFillRect, aSamplingFilter, aTransform);
150 return false;
153 gfxPatternDrawable::gfxPatternDrawable(gfxPattern* aPattern,
154 const IntSize aSize)
155 : gfxDrawable(aSize), mPattern(aPattern) {}
157 gfxPatternDrawable::~gfxPatternDrawable() = default;
159 class DrawingCallbackFromDrawable : public gfxDrawingCallback {
160 public:
161 explicit DrawingCallbackFromDrawable(gfxDrawable* aDrawable)
162 : mDrawable(aDrawable) {
163 NS_ASSERTION(aDrawable, "aDrawable is null!");
166 virtual ~DrawingCallbackFromDrawable() = default;
168 bool operator()(gfxContext* aContext, const gfxRect& aFillRect,
169 const SamplingFilter aSamplingFilter,
170 const gfxMatrix& aTransform = gfxMatrix()) override {
171 return mDrawable->Draw(aContext, aFillRect, ExtendMode::CLAMP,
172 aSamplingFilter, 1.0, aTransform);
175 private:
176 RefPtr<gfxDrawable> mDrawable;
179 already_AddRefed<gfxCallbackDrawable>
180 gfxPatternDrawable::MakeCallbackDrawable() {
181 RefPtr<gfxDrawingCallback> callback = new DrawingCallbackFromDrawable(this);
182 RefPtr<gfxCallbackDrawable> callbackDrawable =
183 new gfxCallbackDrawable(callback, mSize);
184 return callbackDrawable.forget();
187 bool gfxPatternDrawable::Draw(gfxContext* aContext, const gfxRect& aFillRect,
188 ExtendMode aExtendMode,
189 const SamplingFilter aSamplingFilter,
190 gfxFloat aOpacity, const gfxMatrix& aTransform) {
191 DrawTarget& aDrawTarget = *aContext->GetDrawTarget();
193 if (!mPattern) return false;
195 if (IsRepeatingExtendMode(aExtendMode)) {
196 // We can't use mPattern directly: We want our repeated tiles to have
197 // the size mSize, which might not be the case in mPattern.
198 // So we need to draw mPattern into a surface of size mSize, create
199 // a pattern from the surface and draw that pattern.
200 // gfxCallbackDrawable and gfxSurfaceDrawable already know how to do
201 // those things, so we use them here. Drawing mPattern into the surface
202 // will happen through this Draw() method with aRepeat = false.
203 RefPtr<gfxCallbackDrawable> callbackDrawable = MakeCallbackDrawable();
204 return callbackDrawable->Draw(aContext, aFillRect, aExtendMode,
205 aSamplingFilter, aOpacity, aTransform);
208 gfxMatrix oldMatrix = mPattern->GetMatrix();
209 mPattern->SetMatrix(aTransform * oldMatrix);
210 DrawOptions drawOptions(aOpacity);
211 aDrawTarget.FillRect(ToRect(aFillRect), *mPattern->GetPattern(&aDrawTarget),
212 drawOptions);
213 mPattern->SetMatrix(oldMatrix);
214 return true;