diff -r 000000000000 -r 6474c204b198 gfx/thebes/gfxDrawable.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gfx/thebes/gfxDrawable.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,309 @@ +/* -*- 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 "gfxImageSurface.h" +#include "gfxPlatform.h" +#include "gfxColor.h" +#ifdef MOZ_X11 +#include "cairo.h" +#include "gfxXlibSurface.h" +#endif + +using namespace mozilla; +using namespace mozilla::gfx; + +gfxSurfaceDrawable::gfxSurfaceDrawable(gfxASurface* aSurface, + const gfxIntSize aSize, + const gfxMatrix aTransform) + : gfxDrawable(aSize) + , mSurface(aSurface) + , mTransform(aTransform) +{ +} + +gfxSurfaceDrawable::gfxSurfaceDrawable(DrawTarget* aDrawTarget, + const gfxIntSize aSize, + const gfxMatrix aTransform) + : gfxDrawable(aSize) + , mDrawTarget(aDrawTarget) + , mTransform(aTransform) +{ +} + +gfxSurfaceDrawable::gfxSurfaceDrawable(SourceSurface* aSurface, + const gfxIntSize aSize, + const gfxMatrix aTransform) + : gfxDrawable(aSize) + , mSourceSurface(aSurface) + , mTransform(aTransform) +{ +} + +static gfxMatrix +DeviceToImageTransform(gfxContext* aContext, + const gfxMatrix& aUserSpaceToImageSpace) +{ + gfxFloat deviceX, deviceY; + nsRefPtr currentTarget = + aContext->CurrentSurface(&deviceX, &deviceY); + gfxMatrix currentMatrix = aContext->CurrentMatrix(); + gfxMatrix deviceToUser = gfxMatrix(currentMatrix).Invert(); + deviceToUser.Translate(-gfxPoint(-deviceX, -deviceY)); + return gfxMatrix(deviceToUser).Multiply(aUserSpaceToImageSpace); +} + +static void +PreparePatternForUntiledDrawing(gfxPattern* aPattern, + const gfxMatrix& aDeviceToImage, + gfxASurface *currentTarget, + const GraphicsFilter aDefaultFilter) +{ + if (!currentTarget) { + // This happens if we're dealing with an Azure target. + aPattern->SetExtend(gfxPattern::EXTEND_PAD); + aPattern->SetFilter(aDefaultFilter); + return; + } + + // In theory we can handle this using cairo's EXTEND_PAD, + // but implementation limitations mean we have to consult + // the surface type. + switch (currentTarget->GetType()) { + +#ifdef MOZ_X11 + case gfxSurfaceType::Xlib: + { + // See bugs 324698, 422179, and 468496. This is a workaround for + // XRender's RepeatPad not being implemented correctly on old X + // servers. + // + // In this situation, cairo avoids XRender and instead reads back + // to perform EXTEND_PAD with pixman. This is too slow so we + // avoid EXTEND_PAD and set the filter to CAIRO_FILTER_FAST --- + // otherwise, pixman's sampling will sample transparency for the + // outside edges and we'll get blurry edges. + // + // But don't do this for simple downscales because it's horrible. + // Downscaling means that device-space coordinates are + // scaled *up* to find the image pixel coordinates. + // + // Cairo, and hence Gecko, can use RepeatPad on Xorg 1.7. We + // enable EXTEND_PAD provided that we're running on a recent + // enough X server. + if (static_cast(currentTarget)->IsPadSlow()) { + bool isDownscale = + aDeviceToImage.xx >= 1.0 && aDeviceToImage.yy >= 1.0 && + aDeviceToImage.xy == 0.0 && aDeviceToImage.yx == 0.0; + + GraphicsFilter filter = + isDownscale ? aDefaultFilter : (const GraphicsFilter)GraphicsFilter::FILTER_FAST; + aPattern->SetFilter(filter); + + // Use the default EXTEND_NONE + break; + } + // else fall through to EXTEND_PAD and the default filter. + } +#endif + + default: + // turn on EXTEND_PAD. + // This is what we really want for all surface types, if the + // implementation was universally good. + aPattern->SetExtend(gfxPattern::EXTEND_PAD); + aPattern->SetFilter(aDefaultFilter); + break; + } +} + +bool +gfxSurfaceDrawable::Draw(gfxContext* aContext, + const gfxRect& aFillRect, + bool aRepeat, + const GraphicsFilter& aFilter, + const gfxMatrix& aTransform) +{ + nsRefPtr pattern; + if (mDrawTarget) { + if (aContext->IsCairo()) { + nsRefPtr source = + gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mDrawTarget); + pattern = new gfxPattern(source); + } else { + RefPtr source = mDrawTarget->Snapshot(); + pattern = new gfxPattern(source, Matrix()); + } + } else if (mSourceSurface) { + pattern = new gfxPattern(mSourceSurface, Matrix()); + } else { + pattern = new gfxPattern(mSurface); + } + if (aRepeat) { + pattern->SetExtend(gfxPattern::EXTEND_REPEAT); + pattern->SetFilter(aFilter); + } else { + GraphicsFilter filter = aFilter; + if (aContext->CurrentMatrix().HasOnlyIntegerTranslation() && + aTransform.HasOnlyIntegerTranslation()) + { + // If we only have integer translation, no special filtering needs to + // happen and we explicitly use FILTER_FAST. This is fast for some + // backends. + filter = GraphicsFilter::FILTER_FAST; + } + nsRefPtr currentTarget = aContext->CurrentSurface(); + gfxMatrix deviceSpaceToImageSpace = + DeviceToImageTransform(aContext, aTransform); + PreparePatternForUntiledDrawing(pattern, deviceSpaceToImageSpace, + currentTarget, filter); + } + pattern->SetMatrix(gfxMatrix(aTransform).Multiply(mTransform)); + aContext->NewPath(); + aContext->SetPattern(pattern); + aContext->Rectangle(aFillRect); + aContext->Fill(); + // clear the pattern so that the snapshot is released before the + // drawable is destroyed + aContext->SetDeviceColor(gfxRGBA(0.0, 0.0, 0.0, 0.0)); + return true; +} + +already_AddRefed +gfxSurfaceDrawable::GetAsImageSurface() +{ + if (mDrawTarget || mSourceSurface) { + // TODO: Find a way to implement this. The caller really wants a 'sub-image' of + // the original, without having to do a copy. GetDataSurface() might just copy, + // which isn't useful. + return nullptr; + + } + return mSurface->GetAsImageSurface(); +} + +gfxCallbackDrawable::gfxCallbackDrawable(gfxDrawingCallback* aCallback, + const gfxIntSize aSize) + : gfxDrawable(aSize) + , mCallback(aCallback) +{ +} + +already_AddRefed +gfxCallbackDrawable::MakeSurfaceDrawable(const GraphicsFilter aFilter) +{ + SurfaceFormat format = + gfxPlatform::GetPlatform()->Optimal2DFormatForContent(gfxContentType::COLOR_ALPHA); + RefPtr dt = + gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(mSize.ToIntSize(), + format); + if (!dt) + return nullptr; + + nsRefPtr ctx = new gfxContext(dt); + Draw(ctx, gfxRect(0, 0, mSize.width, mSize.height), false, aFilter); + + RefPtr surface = dt->Snapshot(); + nsRefPtr drawable = new gfxSurfaceDrawable(surface, mSize); + return drawable.forget(); +} + +bool +gfxCallbackDrawable::Draw(gfxContext* aContext, + const gfxRect& aFillRect, + bool aRepeat, + const GraphicsFilter& aFilter, + const gfxMatrix& aTransform) +{ + if (aRepeat && !mSurfaceDrawable) { + mSurfaceDrawable = MakeSurfaceDrawable(aFilter); + } + + if (mSurfaceDrawable) + return mSurfaceDrawable->Draw(aContext, aFillRect, aRepeat, aFilter, + aTransform); + + if (mCallback) + return (*mCallback)(aContext, aFillRect, aFilter, aTransform); + + return false; +} + +gfxPatternDrawable::gfxPatternDrawable(gfxPattern* aPattern, + const gfxIntSize aSize) + : gfxDrawable(aSize) + , mPattern(aPattern) +{ +} + +gfxPatternDrawable::~gfxPatternDrawable() +{ +} + +class DrawingCallbackFromDrawable : public gfxDrawingCallback { +public: + DrawingCallbackFromDrawable(gfxDrawable* aDrawable) + : mDrawable(aDrawable) { + NS_ASSERTION(aDrawable, "aDrawable is null!"); + } + + virtual ~DrawingCallbackFromDrawable() {} + + virtual bool operator()(gfxContext* aContext, + const gfxRect& aFillRect, + const GraphicsFilter& aFilter, + const gfxMatrix& aTransform = gfxMatrix()) + { + return mDrawable->Draw(aContext, aFillRect, false, aFilter, + aTransform); + } +private: + nsRefPtr mDrawable; +}; + +already_AddRefed +gfxPatternDrawable::MakeCallbackDrawable() +{ + nsRefPtr callback = + new DrawingCallbackFromDrawable(this); + nsRefPtr callbackDrawable = + new gfxCallbackDrawable(callback, mSize); + return callbackDrawable.forget(); +} + +bool +gfxPatternDrawable::Draw(gfxContext* aContext, + const gfxRect& aFillRect, + bool aRepeat, + const GraphicsFilter& aFilter, + const gfxMatrix& aTransform) +{ + if (!mPattern) + return false; + + if (aRepeat) { + // 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. + nsRefPtr callbackDrawable = MakeCallbackDrawable(); + return callbackDrawable->Draw(aContext, aFillRect, true, aFilter, + aTransform); + } + + aContext->NewPath(); + gfxMatrix oldMatrix = mPattern->GetMatrix(); + mPattern->SetMatrix(gfxMatrix(aTransform).Multiply(oldMatrix)); + aContext->SetPattern(mPattern); + aContext->Rectangle(aFillRect); + aContext->Fill(); + mPattern->SetMatrix(oldMatrix); + return true; +}