diff -r 000000000000 -r 6474c204b198 gfx/thebes/gfxQuartzNativeDrawing.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gfx/thebes/gfxQuartzNativeDrawing.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,172 @@ +/* -*- 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 "gfxQuartzNativeDrawing.h" +#include "gfxQuartzSurface.h" +#include "gfxPlatform.h" +#include "cairo-quartz.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) +{ + mNativeRect.RoundOut(); +} + +CGContextRef +gfxQuartzNativeDrawing::BeginNativeDrawing() +{ + NS_ASSERTION(!mQuartzSurface, "BeginNativeDrawing called when drawing already in progress"); + + gfxPoint deviceOffset; + nsRefPtr surf; + + if (!mContext->IsCairo()) { + DrawTarget *dt = mContext->GetDrawTarget(); + if (dt->GetType() == BackendType::COREGRAPHICS) { + if (dt->IsDualDrawTarget()) { + IntSize backingSize(NSToIntFloor(mNativeRect.width * mBackingScale), + NSToIntFloor(mNativeRect.height * mBackingScale)); + + if (backingSize.IsEmpty()) + return nullptr; + + mDrawTarget = Factory::CreateDrawTarget(BackendType::COREGRAPHICS, backingSize, SurfaceFormat::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; + } + surf = gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(dt); + } else { + 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 + // this to support offscreen drawing fairly easily in the future. + if (surf->GetType() == gfxSurfaceType::Quartz && + (surf->GetContentType() == gfxContentType::COLOR || + (surf->GetContentType() == gfxContentType::COLOR_ALPHA))) { + mQuartzSurface = static_cast(surf.get()); + mSurfaceContext = mContext; + + // grab the CGContextRef + mCGContext = cairo_quartz_get_cg_context_with_clip(mSurfaceContext->GetCairo()); + if (!mCGContext) + return nullptr; + + gfxMatrix m = mContext->CurrentMatrix(); + CGContextTranslateCTM(mCGContext, deviceOffset.x, deviceOffset.y); + + // I -think- that this context will always have an identity + // transform (since we don't maintain a transform on it in + // cairo-land, and instead push/pop as needed) + + gfxFloat x0 = m.x0; + gfxFloat y0 = m.y0; + + // We round x0/y0 if we don't have a scale, because otherwise things get + // rendered badly + // XXX how should we be rounding x0/y0? + if (!m.HasNonTranslationOrFlip()) { + x0 = floor(x0 + 0.5); + y0 = floor(y0 + 0.5); + } + + CGContextConcatCTM(mCGContext, CGAffineTransformMake(m.xx, m.yx, + m.xy, m.yy, + x0, y0)); + + // bug 382049 - need to explicity set the composite operation to sourceOver + CGContextSetCompositeOperation(mCGContext, kPrivateCGCompositeSourceOver); + } else { + nsIntSize backingSize(NSToIntFloor(mNativeRect.width * mBackingScale), + NSToIntFloor(mNativeRect.height * mBackingScale)); + mQuartzSurface = new gfxQuartzSurface(backingSize, + gfxImageFormat::ARGB32); + if (mQuartzSurface->CairoStatus()) + return nullptr; + mSurfaceContext = new gfxContext(mQuartzSurface); + + // grab the CGContextRef + mCGContext = cairo_quartz_get_cg_context_with_clip(mSurfaceContext->GetCairo()); + CGContextScaleCTM(mCGContext, mBackingScale, mBackingScale); + CGContextTranslateCTM(mCGContext, -mNativeRect.X(), -mNativeRect.Y()); + } + + return mCGContext; +} + +void +gfxQuartzNativeDrawing::EndNativeDrawing() +{ + 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(); + if (mSurfaceContext != mContext) { + gfxContextMatrixAutoSaveRestore save(mContext); + + // Copy back to destination + mContext->Translate(mNativeRect.TopLeft()); + mContext->Scale(1.0f / mBackingScale, 1.0f / mBackingScale); + mContext->DrawSurface(mQuartzSurface, mQuartzSurface->GetSize()); + } +}