diff options
Diffstat (limited to 'gfx/thebes/gfxDrawable.cpp')
-rw-r--r-- | gfx/thebes/gfxDrawable.cpp | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/gfx/thebes/gfxDrawable.cpp b/gfx/thebes/gfxDrawable.cpp new file mode 100644 index 000000000..7d25cc975 --- /dev/null +++ b/gfx/thebes/gfxDrawable.cpp @@ -0,0 +1,254 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "gfxDrawable.h" +#include "gfxASurface.h" +#include "gfxContext.h" +#include "gfxPlatform.h" +#include "gfx2DGlue.h" +#ifdef MOZ_X11 +#include "cairo.h" +#include "gfxXlibSurface.h" +#endif +#include "mozilla/gfx/Logging.h" + +using namespace mozilla; +using namespace mozilla::gfx; + +gfxSurfaceDrawable::gfxSurfaceDrawable(SourceSurface* aSurface, + const IntSize aSize, + const gfxMatrix aTransform) + : gfxDrawable(aSize) + , mSourceSurface(aSurface) + , mTransform(aTransform) +{ + if (!mSourceSurface) { + gfxWarning() << "Creating gfxSurfaceDrawable with null SourceSurface"; + } +} + +bool +gfxSurfaceDrawable::DrawWithSamplingRect(DrawTarget* aDrawTarget, + CompositionOp aOp, + AntialiasMode aAntialiasMode, + const gfxRect& aFillRect, + const gfxRect& aSamplingRect, + ExtendMode aExtendMode, + const SamplingFilter aSamplingFilter, + gfxFloat aOpacity) +{ + if (!mSourceSurface) { + return true; + } + + // When drawing with CLAMP we can expand the sampling rect to the nearest pixel + // without changing the result. + IntRect intRect = IntRect::RoundOut(aSamplingRect.x, aSamplingRect.y, + aSamplingRect.width, aSamplingRect.height); + + IntSize size = mSourceSurface->GetSize(); + if (!IntRect(IntPoint(), size).Contains(intRect)) { + return false; + } + + DrawInternal(aDrawTarget, aOp, aAntialiasMode, aFillRect, intRect, + ExtendMode::CLAMP, aSamplingFilter, aOpacity, gfxMatrix()); + return true; +} + +bool +gfxSurfaceDrawable::Draw(gfxContext* aContext, + const gfxRect& aFillRect, + ExtendMode aExtendMode, + const SamplingFilter aSamplingFilter, + gfxFloat aOpacity, + const gfxMatrix& aTransform) + +{ + if (!mSourceSurface) { + return true; + } + + DrawInternal(aContext->GetDrawTarget(), aContext->CurrentOp(), + aContext->CurrentAntialiasMode(), aFillRect, IntRect(), + aExtendMode, aSamplingFilter, aOpacity, aTransform); + return true; +} + +void +gfxSurfaceDrawable::DrawInternal(DrawTarget* aDrawTarget, + CompositionOp aOp, + AntialiasMode aAntialiasMode, + const gfxRect& aFillRect, + const IntRect& aSamplingRect, + ExtendMode aExtendMode, + const SamplingFilter aSamplingFilter, + gfxFloat aOpacity, + const gfxMatrix& aTransform) +{ + Matrix patternTransform = ToMatrix(aTransform * mTransform); + patternTransform.Invert(); + + SurfacePattern pattern(mSourceSurface, aExtendMode, + patternTransform, aSamplingFilter, aSamplingRect); + + Rect fillRect = ToRect(aFillRect); + + if (aOp == CompositionOp::OP_SOURCE && aOpacity == 1.0) { + // Emulate cairo operator source which is bound by mask! + aDrawTarget->ClearRect(fillRect); + aDrawTarget->FillRect(fillRect, pattern); + } else { + aDrawTarget->FillRect(fillRect, pattern, + DrawOptions(aOpacity, aOp, aAntialiasMode)); + } +} + +gfxCallbackDrawable::gfxCallbackDrawable(gfxDrawingCallback* aCallback, + const IntSize aSize) + : gfxDrawable(aSize) + , mCallback(aCallback) +{ +} + +already_AddRefed<gfxSurfaceDrawable> +gfxCallbackDrawable::MakeSurfaceDrawable(const SamplingFilter aSamplingFilter) +{ + SurfaceFormat format = + gfxPlatform::GetPlatform()->Optimal2DFormatForContent(gfxContentType::COLOR_ALPHA); + RefPtr<DrawTarget> dt = + gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(mSize, + format); + if (!dt || !dt->IsValid()) + return nullptr; + + RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt); + MOZ_ASSERT(ctx); // already checked for target above + Draw(ctx, gfxRect(0, 0, mSize.width, mSize.height), ExtendMode::CLAMP, + aSamplingFilter); + + RefPtr<SourceSurface> surface = dt->Snapshot(); + if (surface) { + RefPtr<gfxSurfaceDrawable> drawable = new gfxSurfaceDrawable(surface, mSize); + return drawable.forget(); + } + return nullptr; +} + +static bool +IsRepeatingExtendMode(ExtendMode aExtendMode) +{ + switch (aExtendMode) { + case ExtendMode::REPEAT: + case ExtendMode::REPEAT_X: + case ExtendMode::REPEAT_Y: + return true; + default: + return false; + } +} + +bool +gfxCallbackDrawable::Draw(gfxContext* aContext, + const gfxRect& aFillRect, + ExtendMode aExtendMode, + const SamplingFilter aSamplingFilter, + gfxFloat aOpacity, + const gfxMatrix& aTransform) +{ + if ((IsRepeatingExtendMode(aExtendMode) || aOpacity != 1.0 || aContext->CurrentOp() != CompositionOp::OP_OVER) && + !mSurfaceDrawable) { + mSurfaceDrawable = MakeSurfaceDrawable(aSamplingFilter); + } + + if (mSurfaceDrawable) + return mSurfaceDrawable->Draw(aContext, aFillRect, aExtendMode, + aSamplingFilter, + aOpacity, aTransform); + + if (mCallback) + return (*mCallback)(aContext, aFillRect, aSamplingFilter, aTransform); + + return false; +} + +gfxPatternDrawable::gfxPatternDrawable(gfxPattern* aPattern, + const IntSize aSize) + : gfxDrawable(aSize) + , mPattern(aPattern) +{ +} + +gfxPatternDrawable::~gfxPatternDrawable() +{ +} + +class DrawingCallbackFromDrawable : public gfxDrawingCallback { +public: + explicit DrawingCallbackFromDrawable(gfxDrawable* aDrawable) + : mDrawable(aDrawable) { + NS_ASSERTION(aDrawable, "aDrawable is null!"); + } + + virtual ~DrawingCallbackFromDrawable() {} + + virtual bool operator()(gfxContext* aContext, + const gfxRect& aFillRect, + const SamplingFilter aSamplingFilter, + const gfxMatrix& aTransform = gfxMatrix()) + { + return mDrawable->Draw(aContext, aFillRect, ExtendMode::CLAMP, + aSamplingFilter, 1.0, + aTransform); + } +private: + RefPtr<gfxDrawable> mDrawable; +}; + +already_AddRefed<gfxCallbackDrawable> +gfxPatternDrawable::MakeCallbackDrawable() +{ + RefPtr<gfxDrawingCallback> callback = + new DrawingCallbackFromDrawable(this); + RefPtr<gfxCallbackDrawable> callbackDrawable = + new gfxCallbackDrawable(callback, mSize); + return callbackDrawable.forget(); +} + +bool +gfxPatternDrawable::Draw(gfxContext* aContext, + const gfxRect& aFillRect, + ExtendMode aExtendMode, + const SamplingFilter aSamplingFilter, + gfxFloat aOpacity, + const gfxMatrix& aTransform) +{ + DrawTarget& aDrawTarget = *aContext->GetDrawTarget(); + + if (!mPattern) + return false; + + if (IsRepeatingExtendMode(aExtendMode)) { + // We can't use mPattern directly: We want our repeated tiles to have + // the size mSize, which might not be the case in mPattern. + // So we need to draw mPattern into a surface of size mSize, create + // a pattern from the surface and draw that pattern. + // gfxCallbackDrawable and gfxSurfaceDrawable already know how to do + // those things, so we use them here. Drawing mPattern into the surface + // will happen through this Draw() method with aRepeat = false. + RefPtr<gfxCallbackDrawable> callbackDrawable = MakeCallbackDrawable(); + return callbackDrawable->Draw(aContext, aFillRect, aExtendMode, + aSamplingFilter, + aOpacity, aTransform); + } + + gfxMatrix oldMatrix = mPattern->GetMatrix(); + mPattern->SetMatrix(aTransform * oldMatrix); + DrawOptions drawOptions(aOpacity); + aDrawTarget.FillRect(ToRect(aFillRect), + *mPattern->GetPattern(&aDrawTarget), drawOptions); + mPattern->SetMatrix(oldMatrix); + return true; +} |