diff --git a/gfx/2d/2D.h b/gfx/2d/2D.h index c79ee0f9351..79c8ec6f791 100644 --- a/gfx/2d/2D.h +++ b/gfx/2d/2D.h @@ -839,6 +839,8 @@ public: */ virtual void *GetNativeSurface(NativeSurfaceType aType) { return NULL; } + virtual bool IsDualDrawTarget() { return false; } + void AddUserData(UserDataKey *key, void *userData, void (*destroy)(void*)) { mUserData.Add(key, userData, destroy); } @@ -1012,11 +1014,28 @@ private: class BorrowedCGContext { public: - BorrowedCGContext(DrawTarget *aDT) : mDT(aDT) + BorrowedCGContext() + : cg(nullptr) + , mDT(nullptr) + { } + + BorrowedCGContext(DrawTarget *aDT) + : mDT(aDT) { cg = BorrowCGContextFromDrawTarget(aDT); } + // We can optionally Init after construction in + // case we don't know what the DT will be at construction + // time. + CGContextRef Init(DrawTarget *aDT) + { + MOZ_ASSERT(!mDT, "Can't initialize twice!"); + mDT = aDT; + cg = BorrowCGContextFromDrawTarget(aDT); + return cg; + } + // The caller needs to call Finish if cg is non-null when // they are done with the context. This is currently explicit // instead of happening implicitly in the destructor to make diff --git a/gfx/2d/DrawTargetCG.cpp b/gfx/2d/DrawTargetCG.cpp index d28ac04d159..f67808d2fa1 100644 --- a/gfx/2d/DrawTargetCG.cpp +++ b/gfx/2d/DrawTargetCG.cpp @@ -1195,6 +1195,8 @@ BorrowedCGContext::BorrowCGContextFromDrawTarget(DrawTarget *aDT) // save the state to make it easier for callers to avoid mucking with things CGContextSaveGState(cg); + CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(cgDT->mTransform)); + return cg; } return nullptr; diff --git a/gfx/2d/DrawTargetDual.h b/gfx/2d/DrawTargetDual.h index 8f519603a19..d7dd35295e3 100644 --- a/gfx/2d/DrawTargetDual.h +++ b/gfx/2d/DrawTargetDual.h @@ -131,6 +131,11 @@ public: { return nullptr; } + + virtual bool IsDualDrawTarget() + { + return true; + } private: RefPtr mA; diff --git a/gfx/thebes/gfxQuartzNativeDrawing.cpp b/gfx/thebes/gfxQuartzNativeDrawing.cpp index ee638f4cac3..e04c9f02e8e 100644 --- a/gfx/thebes/gfxQuartzNativeDrawing.cpp +++ b/gfx/thebes/gfxQuartzNativeDrawing.cpp @@ -8,11 +8,15 @@ #include "gfxQuartzNativeDrawing.h" #include "gfxQuartzSurface.h" #include "cairo-quartz.h" +#include "mozilla/gfx/2D.h" // see cairo-quartz-surface.c for the complete list of these enum { kPrivateCGCompositeSourceOver = 2 }; +using namespace mozilla::gfx; +using namespace mozilla; + // private Quartz routine needed here extern "C" { CG_EXTERN void CGContextSetCompositeOperation(CGContextRef, int); @@ -21,7 +25,9 @@ extern "C" { gfxQuartzNativeDrawing::gfxQuartzNativeDrawing(gfxContext* ctx, const gfxRect& nativeRect, gfxFloat aBackingScale) - : mContext(ctx), mNativeRect(nativeRect), mBackingScale(aBackingScale) + : mContext(ctx) + , mNativeRect(nativeRect) + , mBackingScale(aBackingScale) { mNativeRect.RoundOut(); } @@ -31,6 +37,26 @@ gfxQuartzNativeDrawing::BeginNativeDrawing() { NS_ASSERTION(!mQuartzSurface, "BeginNativeDrawing called when drawing already in progress"); + if (!mContext->IsCairo()) { + DrawTarget *dt = mContext->GetDrawTarget(); + if (mContext->GetDrawTarget()->IsDualDrawTarget()) { + IntSize backingSize(NSToIntFloor(mNativeRect.width * mBackingScale), + NSToIntFloor(mNativeRect.height * mBackingScale)); + mDrawTarget = Factory::CreateDrawTarget(BACKEND_COREGRAPHICS, backingSize, FORMAT_B8G8R8A8); + + Matrix transform; + transform.Scale(mBackingScale, mBackingScale); + transform.Translate(-mNativeRect.x, -mNativeRect.y); + + mDrawTarget->SetTransform(transform); + dt = mDrawTarget; + } + + mCGContext = mBorrowedContext.Init(dt); + MOZ_ASSERT(mCGContext); + return mCGContext; + } + gfxPoint deviceOffset; nsRefPtr surf = mContext->CurrentSurface(&deviceOffset.x, &deviceOffset.y); if (!surf || surf->CairoStatus()) @@ -96,7 +122,34 @@ gfxQuartzNativeDrawing::BeginNativeDrawing() void gfxQuartzNativeDrawing::EndNativeDrawing() { - NS_ASSERTION(mQuartzSurface, "EndNativeDrawing called without BeginNativeDrawing"); + NS_ASSERTION(mCGContext, "EndNativeDrawing called without BeginNativeDrawing"); + + if (mBorrowedContext.cg) { + MOZ_ASSERT(!mContext->IsCairo()); + mBorrowedContext.Finish(); + if (mDrawTarget) { + DrawTarget *dest = mContext->GetDrawTarget(); + RefPtr source = mDrawTarget->Snapshot(); + + IntSize backingSize(NSToIntFloor(mNativeRect.width * mBackingScale), + NSToIntFloor(mNativeRect.height * mBackingScale)); + + Matrix oldTransform = dest->GetTransform(); + Matrix newTransform = oldTransform; + newTransform.Translate(mNativeRect.x, mNativeRect.y); + newTransform.Scale(1.0f / mBackingScale, 1.0f / mBackingScale); + + dest->SetTransform(newTransform); + + dest->DrawSurface(source, + gfx::Rect(0, 0, backingSize.width, backingSize.height), + gfx::Rect(0, 0, backingSize.width, backingSize.height)); + + + dest->SetTransform(oldTransform); + } + return; + } cairo_quartz_finish_cg_context_with_clip(mSurfaceContext->GetCairo()); mQuartzSurface->MarkDirty(); diff --git a/gfx/thebes/gfxQuartzNativeDrawing.h b/gfx/thebes/gfxQuartzNativeDrawing.h index 82641c43d3f..3650d5b2920 100644 --- a/gfx/thebes/gfxQuartzNativeDrawing.h +++ b/gfx/thebes/gfxQuartzNativeDrawing.h @@ -56,8 +56,11 @@ private: gfxQuartzNativeDrawing(const gfxQuartzNativeDrawing&) MOZ_DELETE; const gfxQuartzNativeDrawing& operator=(const gfxQuartzNativeDrawing&) MOZ_DELETE; + // Final destination context nsRefPtr mContext; + mozilla::RefPtr mDrawTarget; + mozilla::gfx::BorrowedCGContext mBorrowedContext; // context that draws to mQuartzSurface; can be different from mContext // if mContext is not drawing to Quartz nsRefPtr mSurfaceContext; diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm index 3daba11129d..b70a3f7dacf 100644 --- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -2105,7 +2105,9 @@ nsChildView::MaybeDrawResizeIndicator(GLManager* aManager, const nsIntRect& aRec nsIntSize size = mResizeIndicatorRect.Size(); mResizerImage->UpdateIfNeeded(size, nsIntRegion(), ^(gfx::DrawTarget* drawTarget, const nsIntRegion& updateRegion) { ClearRegion(drawTarget, updateRegion); - DrawResizer(static_cast(drawTarget->GetNativeSurface(gfx::NATIVE_SURFACE_CGCONTEXT))); + gfx::BorrowedCGContext borrow(drawTarget); + DrawResizer(borrow.cg); + borrow.Finish(); }); mResizerImage->Draw(aManager, mResizeIndicatorRect.TopLeft()); @@ -2165,9 +2167,8 @@ nsChildView::UpdateTitlebarImageBuffer() ClearRegion(mTitlebarImageBuffer, dirtyTitlebarRegion); - CGContextRef ctx = - static_cast(mTitlebarImageBuffer->GetNativeSurface(gfx::NATIVE_SURFACE_CGCONTEXT)); - CGContextSaveGState(ctx); + gfx::BorrowedCGContext borrow(mTitlebarImageBuffer); + CGContextRef ctx = borrow.cg; double scale = BackingScaleFactor(); CGContextScaleCTM(ctx, scale, scale); @@ -2233,7 +2234,7 @@ nsChildView::UpdateTitlebarImageBuffer() DevPixelsToCocoaPoints(1)); [NSGraphicsContext setCurrentContext:oldContext]; - CGContextRestoreGState(ctx); + borrow.Finish(); mUpdatedTitlebarRegion.Or(mUpdatedTitlebarRegion, dirtyTitlebarRegion); } @@ -2290,8 +2291,9 @@ nsChildView::MaybeDrawRoundedCorners(GLManager* aManager, const nsIntRect& aRect nsIntSize size(mDevPixelCornerRadius, mDevPixelCornerRadius); mCornerMaskImage->UpdateIfNeeded(size, nsIntRegion(), ^(gfx::DrawTarget* drawTarget, const nsIntRegion& updateRegion) { ClearRegion(drawTarget, updateRegion); - DrawTopLeftCornerMask(static_cast(drawTarget->GetNativeSurface(gfx::NATIVE_SURFACE_CGCONTEXT)), - mDevPixelCornerRadius); + gfx::BorrowedCGContext borrow(drawTarget); + DrawTopLeftCornerMask(borrow.cg, mDevPixelCornerRadius); + borrow.Finish(); }); // Use operator destination in: multiply all 4 channels with source alpha.