diff --git a/gfx/2d/DrawTargetCairo.cpp b/gfx/2d/DrawTargetCairo.cpp index 82d18eb1f77c..cc03dd9ac64a 100644 --- a/gfx/2d/DrawTargetCairo.cpp +++ b/gfx/2d/DrawTargetCairo.cpp @@ -1234,6 +1234,18 @@ DrawTargetCairo::Fill(const Path *aPath, DrawPattern(aPattern, StrokeOptions(), aOptions, DRAW_FILL); } +bool +DrawTargetCairo::IsCurrentGroupOpaque() +{ + cairo_surface_t* surf = cairo_get_group_target(mContext); + + if (!surf) { + return false; + } + + return cairo_surface_get_content(surf) == CAIRO_CONTENT_COLOR; +} + void DrawTargetCairo::SetPermitSubpixelAA(bool aPermitSubpixelAA) { @@ -1455,6 +1467,87 @@ DrawTargetCairo::PopClip() cairo_set_matrix(mContext, &mat); } + +void +DrawTargetCairo::PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask, + const Matrix& aMaskTransform, const IntRect& aBounds, + bool aCopyBackground) +{ + cairo_content_t content = CAIRO_CONTENT_COLOR_ALPHA; + + if (mFormat == SurfaceFormat::A8) { + content = CAIRO_CONTENT_ALPHA; + } else if (aOpaque) { + content = CAIRO_CONTENT_COLOR; + } + + if (aCopyBackground) { + cairo_surface_t* source = cairo_get_group_target(mContext); + cairo_push_group_with_content(mContext, content); + cairo_surface_t* dest = cairo_get_group_target(mContext); + cairo_t* ctx = cairo_create(dest); + cairo_set_source_surface(ctx, source, 0, 0); + cairo_set_operator(ctx, CAIRO_OPERATOR_SOURCE); + cairo_paint(ctx); + cairo_destroy(ctx); + } else { + cairo_push_group_with_content(mContext, content); + } + + PushedLayer layer(aOpacity, mPermitSubpixelAA); + + if (aMask) { + cairo_surface_t* surf = GetCairoSurfaceForSourceSurface(aMask); + if (surf) { + layer.mMaskPattern = cairo_pattern_create_for_surface(surf); + cairo_matrix_t mat; + GfxMatrixToCairoMatrix(aMaskTransform, mat); + cairo_matrix_invert(&mat); + cairo_pattern_set_matrix(layer.mMaskPattern, &mat); + cairo_surface_destroy(surf); + } else { + gfxCriticalError() << "Failed to get cairo surface for mask surface!"; + } + } + + mPushedLayers.push_back(layer); + + SetPermitSubpixelAA(aOpaque); +} + +void +DrawTargetCairo::PopLayer() +{ + MOZ_ASSERT(mPushedLayers.size()); + + cairo_set_operator(mContext, CAIRO_OPERATOR_OVER); + + cairo_pop_group_to_source(mContext); + + PushedLayer layer = mPushedLayers.back(); + mPushedLayers.pop_back(); + + if (!layer.mMaskPattern) { + cairo_paint_with_alpha(mContext, layer.mOpacity); + } else { + if (layer.mOpacity != Float(1.0)) { + cairo_push_group_with_content(mContext, CAIRO_CONTENT_COLOR_ALPHA); + + // Now draw the content using the desired operator + cairo_paint_with_alpha(mContext, layer.mOpacity); + + cairo_pop_group_to_source(mContext); + } + cairo_mask(mContext, layer.mMaskPattern); + } + + cairo_matrix_t mat; + GfxMatrixToCairoMatrix(mTransform, mat); + cairo_set_matrix(mContext, &mat); + + cairo_pattern_destroy(layer.mMaskPattern); + SetPermitSubpixelAA(layer.mWasPermittingSubpixelAA); +} already_AddRefed DrawTargetCairo::CreatePathBuilder(FillRule aFillRule /* = FillRule::FILL_WINDING */) const @@ -1772,7 +1865,7 @@ void * DrawTargetCairo::GetNativeSurface(NativeSurfaceType aType) { if (aType == NativeSurfaceType::CAIRO_SURFACE) { - return cairo_get_target(mContext); + return cairo_get_group_target(mContext); } if (aType == NativeSurfaceType::CAIRO_CONTEXT) { return mContext; diff --git a/gfx/2d/DrawTargetCairo.h b/gfx/2d/DrawTargetCairo.h index 4fdd25de91ad..b08183d12819 100644 --- a/gfx/2d/DrawTargetCairo.h +++ b/gfx/2d/DrawTargetCairo.h @@ -65,6 +65,8 @@ public: virtual already_AddRefed Snapshot() override; virtual IntSize GetSize() override; + virtual bool IsCurrentGroupOpaque() override; + virtual void SetPermitSubpixelAA(bool aPermitSubpixelAA) override; virtual bool LockBits(uint8_t** aData, IntSize* aSize, @@ -134,6 +136,12 @@ public: virtual void PushClip(const Path *aPath) override; virtual void PushClipRect(const Rect &aRect) override; virtual void PopClip() override; + virtual void PushLayer(bool aOpaque, Float aOpacity, + SourceSurface* aMask, + const Matrix& aMaskTransform, + const IntRect& aBounds = IntRect(), + bool aCopyBackground = false) override; + virtual void PopLayer() override; virtual already_AddRefed CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override; @@ -214,6 +222,19 @@ private: // data uint8_t* mLockedBits; + struct PushedLayer + { + PushedLayer(Float aOpacity, bool aWasPermittingSubpixelAA) + : mOpacity(aOpacity) + , mMaskPattern(nullptr) + , mWasPermittingSubpixelAA(aWasPermittingSubpixelAA) + {} + Float mOpacity; + cairo_pattern_t* mMaskPattern; + bool mWasPermittingSubpixelAA; + }; + std::vector mPushedLayers; + // The latest snapshot of this surface. This needs to be told when this // target is modified. We keep it alive as a cache. RefPtr mSnapshot; diff --git a/gfx/thebes/gfxContext.cpp b/gfx/thebes/gfxContext.cpp index fa278807e1f3..52afbe194d60 100644 --- a/gfx/thebes/gfxContext.cpp +++ b/gfx/thebes/gfxContext.cpp @@ -115,8 +115,10 @@ gfxContext::CurrentSurface(gfxFloat *dx, gfxFloat *dy) (cairo_surface_t*)mDT->GetNativeSurface(NativeSurfaceType::CAIRO_SURFACE); if (s) { if (dx && dy) { - *dx = -CurrentState().deviceOffset.x; - *dy = -CurrentState().deviceOffset.y; + double sdx, sdy; + cairo_surface_get_device_offset(s, &sdx, &sdy); + *dx = -CurrentState().deviceOffset.x + sdx; + *dy = -CurrentState().deviceOffset.y + sdy; } return gfxASurface::Wrap(s); }