Bug 1220629 - Part 3: Implement PushLayer/PopLayer API in cairo. r=jrmuizel
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -1229,16 +1229,28 @@ DrawTargetCairo::Fill(const Path *aPath,
return;
PathCairo* path = const_cast<PathCairo*>(static_cast<const PathCairo*>(aPath));
path->SetPathOnContext(mContext);
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)
{
DrawTarget::SetPermitSubpixelAA(aPermitSubpixelAA);
#ifdef MOZ_TREE_CAIRO
cairo_surface_set_subpixel_antialiasing(mSurface,
aPermitSubpixelAA ? CAIRO_SUBPIXEL_ANTIALIASING_ENABLED : CAIRO_SUBPIXEL_ANTIALIASING_DISABLED);
#endif
@@ -1450,16 +1462,97 @@ DrawTargetCairo::PopClip()
// so we'll save it now and restore it after the cairo_restore
cairo_matrix_t mat;
cairo_get_matrix(mContext, &mat);
cairo_restore(mContext);
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<PathBuilder>
DrawTargetCairo::CreatePathBuilder(FillRule aFillRule /* = FillRule::FILL_WINDING */) const
{
return MakeAndAddRef<PathBuilderCairo>(aFillRule);
}
void
@@ -1767,17 +1860,17 @@ DrawTargetCairo::Init(unsigned char* aDa
aStride);
return InitAlreadyReferenced(surf, aSize);
}
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;
}
return nullptr;
}
--- a/gfx/2d/DrawTargetCairo.h
+++ b/gfx/2d/DrawTargetCairo.h
@@ -60,16 +60,18 @@ public:
virtual ~DrawTargetCairo();
virtual bool IsValid() const override;
virtual DrawTargetType GetType() const override;
virtual BackendType GetBackendType() const override { return BackendType::CAIRO; }
virtual already_AddRefed<SourceSurface> Snapshot() override;
virtual IntSize GetSize() override;
+ virtual bool IsCurrentGroupOpaque() override;
+
virtual void SetPermitSubpixelAA(bool aPermitSubpixelAA) override;
virtual bool LockBits(uint8_t** aData, IntSize* aSize,
int32_t* aStride, SurfaceFormat* aFormat) override;
virtual void ReleaseBits(uint8_t* aData) override;
virtual void Flush() override;
virtual void DrawSurface(SourceSurface *aSurface,
@@ -129,16 +131,22 @@ public:
virtual void MaskSurface(const Pattern &aSource,
SourceSurface *aMask,
Point aOffset,
const DrawOptions &aOptions = DrawOptions()) override;
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<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override;
virtual already_AddRefed<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
const IntSize &aSize,
int32_t aStride,
SurfaceFormat aFormat) const override;
virtual already_AddRefed<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const override;
@@ -209,16 +217,29 @@ private: // methods
private: // data
cairo_t* mContext;
cairo_surface_t* mSurface;
IntSize mSize;
bool mTransformSingular;
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<PushedLayer> 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<SourceSurfaceCairo> mSnapshot;
static cairo_surface_t *mDummySurface;
};
} // namespace gfx
} // namespace mozilla
--- a/gfx/thebes/gfxContext.cpp
+++ b/gfx/thebes/gfxContext.cpp
@@ -110,18 +110,20 @@ gfxContext::~gfxContext()
already_AddRefed<gfxASurface>
gfxContext::CurrentSurface(gfxFloat *dx, gfxFloat *dy)
{
if (mDT->GetBackendType() == BackendType::CAIRO) {
cairo_surface_t *s =
(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);
}
}
if (dx && dy) {
*dx = *dy = 0;
}