Bug 892966 - Make gfxQuartzNativeDrawing support Moz2D surfaces. r=jrmuizel
authorMatt Woodrow <mwoodrow@mozilla.com>
Fri, 12 Jul 2013 17:19:29 -0400
changeset 139165 71ae24eae0d1f71e77aa7551133e17a40990ff87
parent 139164 c14de13134bf4e0ea1f5a3632f7b439a828c4a9a
child 139166 120178591e35354caad76cecb3add58428f92c72
push idunknown
push userunknown
push dateunknown
reviewersjrmuizel
bugs892966
milestone25.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 892966 - Make gfxQuartzNativeDrawing support Moz2D surfaces. r=jrmuizel
gfx/2d/2D.h
gfx/2d/DrawTargetCG.cpp
gfx/2d/DrawTargetDual.h
gfx/thebes/gfxQuartzNativeDrawing.cpp
gfx/thebes/gfxQuartzNativeDrawing.h
widget/cocoa/nsChildView.mm
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -834,16 +834,18 @@ public:
 
   SurfaceFormat GetFormat() { return mFormat; }
 
   /* Tries to get a native surface for a DrawTarget, this may fail if the
    * draw target cannot convert to this surface type.
    */
   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);
   }
   void *GetUserData(UserDataKey *key) {
     return mUserData.Get(key);
   }
 
   /* Within this rectangle all pixels will be opaque by the time the result of
@@ -1007,21 +1009,38 @@ private:
  * DrawTargetCG. This is used for drawing themed widgets.
  *
  * Callers should check the cg member after constructing the object
  * to see if it succeeded. The DrawTarget should not be used while
  * the context is borrowed. */
 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
   // what's happening in the caller more clear. It also
   // let's you resume using the DrawTarget in the same scope.
   void Finish()
   {
     if (cg) {
--- a/gfx/2d/DrawTargetCG.cpp
+++ b/gfx/2d/DrawTargetCG.cpp
@@ -1190,16 +1190,18 @@ BorrowedCGContext::BorrowCGContextFromDr
 
     // swap out the context
     CGContextRef cg = cgDT->mCg;
     cgDT->mCg = nullptr;
 
     // 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;
 }
 
 void
 BorrowedCGContext::ReturnCGContextToDrawTarget(DrawTarget *aDT, CGContextRef cg)
 {
--- a/gfx/2d/DrawTargetDual.h
+++ b/gfx/2d/DrawTargetDual.h
@@ -126,16 +126,21 @@ public:
   {
     return mA->CreateGradientStops(aStops, aNumStops, aExtendMode);
   }
      
   virtual void *GetNativeSurface(NativeSurfaceType aType)
   {
     return nullptr;
   }
+
+  virtual bool IsDualDrawTarget()
+  {
+    return true;
+  }
      
 private:
   RefPtr<DrawTarget> mA;
   RefPtr<DrawTarget> mB;
 };
      
 }
 }
--- a/gfx/thebes/gfxQuartzNativeDrawing.cpp
+++ b/gfx/thebes/gfxQuartzNativeDrawing.cpp
@@ -3,39 +3,65 @@
  * 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 "nsMathUtils.h"
 
 #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);
 }
 
 gfxQuartzNativeDrawing::gfxQuartzNativeDrawing(gfxContext* ctx,
                                                const gfxRect& nativeRect,
                                                gfxFloat aBackingScale)
-    : mContext(ctx), mNativeRect(nativeRect), mBackingScale(aBackingScale)
+    : mContext(ctx)
+    , mNativeRect(nativeRect)
+    , mBackingScale(aBackingScale)
 {
     mNativeRect.RoundOut();
 }
 
 CGContextRef
 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<gfxASurface> surf = mContext->CurrentSurface(&deviceOffset.x, &deviceOffset.y);
     if (!surf || surf->CairoStatus())
         return nullptr;
 
     // if this is a native Quartz surface, we don't have to redirect
     // rendering to our own CGContextRef; in most cases, we are able to
     // use the CGContextRef from the surface directly.  we can extend
@@ -91,17 +117,44 @@ gfxQuartzNativeDrawing::BeginNativeDrawi
     }
 
     return mCGContext;
 }
 
 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<SourceSurface> 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();
     if (mSurfaceContext != mContext) {
         gfxContextMatrixAutoSaveRestore save(mContext);
 
         // Copy back to destination
         mContext->Translate(mNativeRect.TopLeft());
--- a/gfx/thebes/gfxQuartzNativeDrawing.h
+++ b/gfx/thebes/gfxQuartzNativeDrawing.h
@@ -51,18 +51,21 @@ public:
     /* Marks the end of native drawing */
     void EndNativeDrawing();
 
 private:
     // don't allow copying via construction or assignment
     gfxQuartzNativeDrawing(const gfxQuartzNativeDrawing&) MOZ_DELETE;
     const gfxQuartzNativeDrawing& operator=(const gfxQuartzNativeDrawing&) MOZ_DELETE;
 
+
     // Final destination context
     nsRefPtr<gfxContext> mContext;
+    mozilla::RefPtr<mozilla::gfx::DrawTarget> mDrawTarget;
+    mozilla::gfx::BorrowedCGContext mBorrowedContext;
     // context that draws to mQuartzSurface; can be different from mContext
     // if mContext is not drawing to Quartz
     nsRefPtr<gfxContext> mSurfaceContext;
     gfxRect mNativeRect;
     gfxFloat mBackingScale;
 
     // saved state
     nsRefPtr<gfxQuartzSurface> mQuartzSurface;
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -2100,17 +2100,19 @@ nsChildView::MaybeDrawResizeIndicator(GL
 
   if (!mResizerImage) {
     mResizerImage = new RectTextureImage(aManager->gl());
   }
 
   nsIntSize size = mResizeIndicatorRect.Size();
   mResizerImage->UpdateIfNeeded(size, nsIntRegion(), ^(gfx::DrawTarget* drawTarget, const nsIntRegion& updateRegion) {
     ClearRegion(drawTarget, updateRegion);
-    DrawResizer(static_cast<CGContextRef>(drawTarget->GetNativeSurface(gfx::NATIVE_SURFACE_CGCONTEXT)));
+    gfx::BorrowedCGContext borrow(drawTarget);
+    DrawResizer(borrow.cg);
+    borrow.Finish();
   });
 
   mResizerImage->Draw(aManager, mResizeIndicatorRect.TopLeft());
 }
 
 // Draw the highlight line at the top of the titlebar.
 // This function draws into the current NSGraphicsContext and assumes flippedness.
 static void
@@ -2160,19 +2162,18 @@ nsChildView::UpdateTitlebarImageBuffer()
                                      gfx::FORMAT_B8G8R8A8);
   }
 
   if (dirtyTitlebarRegion.IsEmpty())
     return;
 
   ClearRegion(mTitlebarImageBuffer, dirtyTitlebarRegion);
 
-  CGContextRef ctx =
-    static_cast<CGContextRef>(mTitlebarImageBuffer->GetNativeSurface(gfx::NATIVE_SURFACE_CGCONTEXT));
-  CGContextSaveGState(ctx);
+  gfx::BorrowedCGContext borrow(mTitlebarImageBuffer);
+  CGContextRef ctx = borrow.cg;
 
   double scale = BackingScaleFactor();
   CGContextScaleCTM(ctx, scale, scale);
   NSGraphicsContext* oldContext = [NSGraphicsContext currentContext];
 
   CGContextSaveGState(ctx);
 
   BaseWindow* window = (BaseWindow*)[mView window];
@@ -2228,17 +2229,17 @@ nsChildView::UpdateTitlebarImageBuffer()
   }
 
   CGContextRestoreGState(ctx);
 
   DrawTitlebarHighlight([frameView bounds].size, [(ChildView*)mView cornerRadius],
                         DevPixelsToCocoaPoints(1));
 
   [NSGraphicsContext setCurrentContext:oldContext];
-  CGContextRestoreGState(ctx);
+  borrow.Finish();
 
   mUpdatedTitlebarRegion.Or(mUpdatedTitlebarRegion, dirtyTitlebarRegion);
 }
 
 // This method draws an overlay in the top of the window which contains the
 // titlebar controls (e.g. close, min, zoom, fullscreen) and the titlebar
 // highlight effect.
 // This is necessary because the real titlebar controls are covered by our
@@ -2285,18 +2286,19 @@ nsChildView::MaybeDrawRoundedCorners(GLM
 
   if (!mCornerMaskImage) {
     mCornerMaskImage = new RectTextureImage(aManager->gl());
   }
 
   nsIntSize size(mDevPixelCornerRadius, mDevPixelCornerRadius);
   mCornerMaskImage->UpdateIfNeeded(size, nsIntRegion(), ^(gfx::DrawTarget* drawTarget, const nsIntRegion& updateRegion) {
     ClearRegion(drawTarget, updateRegion);
-    DrawTopLeftCornerMask(static_cast<CGContextRef>(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.
   aManager->gl()->fBlendFuncSeparate(LOCAL_GL_ZERO, LOCAL_GL_SRC_ALPHA,
                                      LOCAL_GL_ZERO, LOCAL_GL_SRC_ALPHA);
 
   gfx3DMatrix flipX = gfx3DMatrix::ScalingMatrix(-1, 1, 1);
   gfx3DMatrix flipY = gfx3DMatrix::ScalingMatrix(1, -1, 1);