gfxDrawable.cpp (7897B)
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 "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" 15 16 using namespace mozilla; 17 using namespace mozilla::gfx; 18 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"; 25 } 26 } 27 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; 35 } 36 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()); 42 43 IntSize size = mSourceSurface->GetSize(); 44 if (!IntRect(IntPoint(), size).Contains(intRect)) { 45 return false; 46 } 47 48 DrawInternal(aDrawTarget, aOp, aAntialiasMode, aFillRect, intRect, 49 ExtendMode::CLAMP, aSamplingFilter, aOpacity, gfxMatrix()); 50 return true; 51 } 52 53 bool gfxSurfaceDrawable::Draw(gfxContext* aContext, const gfxRect& aFillRect, 54 ExtendMode aExtendMode, 55 const SamplingFilter aSamplingFilter, 56 gfxFloat aOpacity, const gfxMatrix& aTransform) 57 58 { 59 if (!mSourceSurface) { 60 return true; 61 } 62 63 DrawInternal(aContext->GetDrawTarget(), aContext->CurrentOp(), 64 aContext->CurrentAntialiasMode(), aFillRect, IntRect(), 65 aExtendMode, aSamplingFilter, aOpacity, aTransform); 66 return true; 67 } 68 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(); 76 77 SurfacePattern pattern(mSourceSurface, aExtendMode, patternTransform, 78 aSamplingFilter, aSamplingRect); 79 80 Rect fillRect = ToRect(aFillRect); 81 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)); 89 } 90 } 91 92 gfxCallbackDrawable::gfxCallbackDrawable(gfxDrawingCallback* aCallback, 93 const IntSize aSize) 94 : gfxDrawable(aSize), mCallback(aCallback) {} 95 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; 102 } 103 RefPtr<DrawTarget> dt = 104 aContext->GetDrawTarget()->CreateSimilarDrawTarget(mSize, format); 105 106 if (!dt || !dt->IsValid()) { 107 return nullptr; 108 } 109 110 gfxContext ctx(dt); 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 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 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 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: 162 explicit DrawingCallbackFromDrawable(gfxDrawable* aDrawable) 163 : mDrawable(aDrawable) { 164 NS_ASSERTION(aDrawable, "aDrawable is null!"); 165 } 166 167 virtual ~DrawingCallbackFromDrawable() = default; 168 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> 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 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 }