/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- * 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 "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 gfxCallbackDrawable::MakeSurfaceDrawable( gfxContext* aContext, const SamplingFilter aSamplingFilter) { SurfaceFormat format = gfxPlatform::GetPlatform()->Optimal2DFormatForContent( gfxContentType::COLOR_ALPHA); if (!aContext->GetDrawTarget()->CanCreateSimilarDrawTarget(mSize, format)) { return nullptr; } RefPtr dt = aContext->GetDrawTarget()->CreateSimilarDrawTarget(mSize, format); if (!dt || !dt->IsValid()) return nullptr; RefPtr 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 surface = dt->Snapshot(); if (surface) { RefPtr 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(aContext, 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() = default; class DrawingCallbackFromDrawable : public gfxDrawingCallback { public: explicit DrawingCallbackFromDrawable(gfxDrawable* aDrawable) : mDrawable(aDrawable) { NS_ASSERTION(aDrawable, "aDrawable is null!"); } virtual ~DrawingCallbackFromDrawable() = default; bool operator()(gfxContext* aContext, const gfxRect& aFillRect, const SamplingFilter aSamplingFilter, const gfxMatrix& aTransform = gfxMatrix()) override { return mDrawable->Draw(aContext, aFillRect, ExtendMode::CLAMP, aSamplingFilter, 1.0, aTransform); } private: RefPtr mDrawable; }; already_AddRefed gfxPatternDrawable::MakeCallbackDrawable() { RefPtr callback = new DrawingCallbackFromDrawable(this); RefPtr 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 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; }