michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "gfxQuartzNativeDrawing.h" michael@0: #include "gfxQuartzSurface.h" michael@0: #include "gfxPlatform.h" michael@0: #include "cairo-quartz.h" michael@0: michael@0: // see cairo-quartz-surface.c for the complete list of these michael@0: enum { michael@0: kPrivateCGCompositeSourceOver = 2 michael@0: }; michael@0: michael@0: using namespace mozilla::gfx; michael@0: using namespace mozilla; michael@0: michael@0: // private Quartz routine needed here michael@0: extern "C" { michael@0: CG_EXTERN void CGContextSetCompositeOperation(CGContextRef, int); michael@0: } michael@0: michael@0: gfxQuartzNativeDrawing::gfxQuartzNativeDrawing(gfxContext* ctx, michael@0: const gfxRect& nativeRect, michael@0: gfxFloat aBackingScale) michael@0: : mContext(ctx) michael@0: , mNativeRect(nativeRect) michael@0: , mBackingScale(aBackingScale) michael@0: { michael@0: mNativeRect.RoundOut(); michael@0: } michael@0: michael@0: CGContextRef michael@0: gfxQuartzNativeDrawing::BeginNativeDrawing() michael@0: { michael@0: NS_ASSERTION(!mQuartzSurface, "BeginNativeDrawing called when drawing already in progress"); michael@0: michael@0: gfxPoint deviceOffset; michael@0: nsRefPtr surf; michael@0: michael@0: if (!mContext->IsCairo()) { michael@0: DrawTarget *dt = mContext->GetDrawTarget(); michael@0: if (dt->GetType() == BackendType::COREGRAPHICS) { michael@0: if (dt->IsDualDrawTarget()) { michael@0: IntSize backingSize(NSToIntFloor(mNativeRect.width * mBackingScale), michael@0: NSToIntFloor(mNativeRect.height * mBackingScale)); michael@0: michael@0: if (backingSize.IsEmpty()) michael@0: return nullptr; michael@0: michael@0: mDrawTarget = Factory::CreateDrawTarget(BackendType::COREGRAPHICS, backingSize, SurfaceFormat::B8G8R8A8); michael@0: michael@0: Matrix transform; michael@0: transform.Scale(mBackingScale, mBackingScale); michael@0: transform.Translate(-mNativeRect.x, -mNativeRect.y); michael@0: michael@0: mDrawTarget->SetTransform(transform); michael@0: dt = mDrawTarget; michael@0: } michael@0: michael@0: mCGContext = mBorrowedContext.Init(dt); michael@0: MOZ_ASSERT(mCGContext); michael@0: return mCGContext; michael@0: } michael@0: surf = gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(dt); michael@0: } else { michael@0: surf = mContext->CurrentSurface(&deviceOffset.x, &deviceOffset.y); michael@0: } michael@0: if (!surf || surf->CairoStatus()) michael@0: return nullptr; michael@0: michael@0: // if this is a native Quartz surface, we don't have to redirect michael@0: // rendering to our own CGContextRef; in most cases, we are able to michael@0: // use the CGContextRef from the surface directly. we can extend michael@0: // this to support offscreen drawing fairly easily in the future. michael@0: if (surf->GetType() == gfxSurfaceType::Quartz && michael@0: (surf->GetContentType() == gfxContentType::COLOR || michael@0: (surf->GetContentType() == gfxContentType::COLOR_ALPHA))) { michael@0: mQuartzSurface = static_cast(surf.get()); michael@0: mSurfaceContext = mContext; michael@0: michael@0: // grab the CGContextRef michael@0: mCGContext = cairo_quartz_get_cg_context_with_clip(mSurfaceContext->GetCairo()); michael@0: if (!mCGContext) michael@0: return nullptr; michael@0: michael@0: gfxMatrix m = mContext->CurrentMatrix(); michael@0: CGContextTranslateCTM(mCGContext, deviceOffset.x, deviceOffset.y); michael@0: michael@0: // I -think- that this context will always have an identity michael@0: // transform (since we don't maintain a transform on it in michael@0: // cairo-land, and instead push/pop as needed) michael@0: michael@0: gfxFloat x0 = m.x0; michael@0: gfxFloat y0 = m.y0; michael@0: michael@0: // We round x0/y0 if we don't have a scale, because otherwise things get michael@0: // rendered badly michael@0: // XXX how should we be rounding x0/y0? michael@0: if (!m.HasNonTranslationOrFlip()) { michael@0: x0 = floor(x0 + 0.5); michael@0: y0 = floor(y0 + 0.5); michael@0: } michael@0: michael@0: CGContextConcatCTM(mCGContext, CGAffineTransformMake(m.xx, m.yx, michael@0: m.xy, m.yy, michael@0: x0, y0)); michael@0: michael@0: // bug 382049 - need to explicity set the composite operation to sourceOver michael@0: CGContextSetCompositeOperation(mCGContext, kPrivateCGCompositeSourceOver); michael@0: } else { michael@0: nsIntSize backingSize(NSToIntFloor(mNativeRect.width * mBackingScale), michael@0: NSToIntFloor(mNativeRect.height * mBackingScale)); michael@0: mQuartzSurface = new gfxQuartzSurface(backingSize, michael@0: gfxImageFormat::ARGB32); michael@0: if (mQuartzSurface->CairoStatus()) michael@0: return nullptr; michael@0: mSurfaceContext = new gfxContext(mQuartzSurface); michael@0: michael@0: // grab the CGContextRef michael@0: mCGContext = cairo_quartz_get_cg_context_with_clip(mSurfaceContext->GetCairo()); michael@0: CGContextScaleCTM(mCGContext, mBackingScale, mBackingScale); michael@0: CGContextTranslateCTM(mCGContext, -mNativeRect.X(), -mNativeRect.Y()); michael@0: } michael@0: michael@0: return mCGContext; michael@0: } michael@0: michael@0: void michael@0: gfxQuartzNativeDrawing::EndNativeDrawing() michael@0: { michael@0: NS_ASSERTION(mCGContext, "EndNativeDrawing called without BeginNativeDrawing"); michael@0: michael@0: if (mBorrowedContext.cg) { michael@0: MOZ_ASSERT(!mContext->IsCairo()); michael@0: mBorrowedContext.Finish(); michael@0: if (mDrawTarget) { michael@0: DrawTarget *dest = mContext->GetDrawTarget(); michael@0: RefPtr source = mDrawTarget->Snapshot(); michael@0: michael@0: IntSize backingSize(NSToIntFloor(mNativeRect.width * mBackingScale), michael@0: NSToIntFloor(mNativeRect.height * mBackingScale)); michael@0: michael@0: Matrix oldTransform = dest->GetTransform(); michael@0: Matrix newTransform = oldTransform; michael@0: newTransform.Translate(mNativeRect.x, mNativeRect.y); michael@0: newTransform.Scale(1.0f / mBackingScale, 1.0f / mBackingScale); michael@0: michael@0: dest->SetTransform(newTransform); michael@0: michael@0: dest->DrawSurface(source, michael@0: gfx::Rect(0, 0, backingSize.width, backingSize.height), michael@0: gfx::Rect(0, 0, backingSize.width, backingSize.height)); michael@0: michael@0: michael@0: dest->SetTransform(oldTransform); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: cairo_quartz_finish_cg_context_with_clip(mSurfaceContext->GetCairo()); michael@0: mQuartzSurface->MarkDirty(); michael@0: if (mSurfaceContext != mContext) { michael@0: gfxContextMatrixAutoSaveRestore save(mContext); michael@0: michael@0: // Copy back to destination michael@0: mContext->Translate(mNativeRect.TopLeft()); michael@0: mContext->Scale(1.0f / mBackingScale, 1.0f / mBackingScale); michael@0: mContext->DrawSurface(mQuartzSurface, mQuartzSurface->GetSize()); michael@0: } michael@0: }