1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/thebes/gfxQuartzNativeDrawing.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,172 @@ 1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "gfxQuartzNativeDrawing.h" 1.10 +#include "gfxQuartzSurface.h" 1.11 +#include "gfxPlatform.h" 1.12 +#include "cairo-quartz.h" 1.13 + 1.14 +// see cairo-quartz-surface.c for the complete list of these 1.15 +enum { 1.16 + kPrivateCGCompositeSourceOver = 2 1.17 +}; 1.18 + 1.19 +using namespace mozilla::gfx; 1.20 +using namespace mozilla; 1.21 + 1.22 +// private Quartz routine needed here 1.23 +extern "C" { 1.24 + CG_EXTERN void CGContextSetCompositeOperation(CGContextRef, int); 1.25 +} 1.26 + 1.27 +gfxQuartzNativeDrawing::gfxQuartzNativeDrawing(gfxContext* ctx, 1.28 + const gfxRect& nativeRect, 1.29 + gfxFloat aBackingScale) 1.30 + : mContext(ctx) 1.31 + , mNativeRect(nativeRect) 1.32 + , mBackingScale(aBackingScale) 1.33 +{ 1.34 + mNativeRect.RoundOut(); 1.35 +} 1.36 + 1.37 +CGContextRef 1.38 +gfxQuartzNativeDrawing::BeginNativeDrawing() 1.39 +{ 1.40 + NS_ASSERTION(!mQuartzSurface, "BeginNativeDrawing called when drawing already in progress"); 1.41 + 1.42 + gfxPoint deviceOffset; 1.43 + nsRefPtr<gfxASurface> surf; 1.44 + 1.45 + if (!mContext->IsCairo()) { 1.46 + DrawTarget *dt = mContext->GetDrawTarget(); 1.47 + if (dt->GetType() == BackendType::COREGRAPHICS) { 1.48 + if (dt->IsDualDrawTarget()) { 1.49 + IntSize backingSize(NSToIntFloor(mNativeRect.width * mBackingScale), 1.50 + NSToIntFloor(mNativeRect.height * mBackingScale)); 1.51 + 1.52 + if (backingSize.IsEmpty()) 1.53 + return nullptr; 1.54 + 1.55 + mDrawTarget = Factory::CreateDrawTarget(BackendType::COREGRAPHICS, backingSize, SurfaceFormat::B8G8R8A8); 1.56 + 1.57 + Matrix transform; 1.58 + transform.Scale(mBackingScale, mBackingScale); 1.59 + transform.Translate(-mNativeRect.x, -mNativeRect.y); 1.60 + 1.61 + mDrawTarget->SetTransform(transform); 1.62 + dt = mDrawTarget; 1.63 + } 1.64 + 1.65 + mCGContext = mBorrowedContext.Init(dt); 1.66 + MOZ_ASSERT(mCGContext); 1.67 + return mCGContext; 1.68 + } 1.69 + surf = gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(dt); 1.70 + } else { 1.71 + surf = mContext->CurrentSurface(&deviceOffset.x, &deviceOffset.y); 1.72 + } 1.73 + if (!surf || surf->CairoStatus()) 1.74 + return nullptr; 1.75 + 1.76 + // if this is a native Quartz surface, we don't have to redirect 1.77 + // rendering to our own CGContextRef; in most cases, we are able to 1.78 + // use the CGContextRef from the surface directly. we can extend 1.79 + // this to support offscreen drawing fairly easily in the future. 1.80 + if (surf->GetType() == gfxSurfaceType::Quartz && 1.81 + (surf->GetContentType() == gfxContentType::COLOR || 1.82 + (surf->GetContentType() == gfxContentType::COLOR_ALPHA))) { 1.83 + mQuartzSurface = static_cast<gfxQuartzSurface*>(surf.get()); 1.84 + mSurfaceContext = mContext; 1.85 + 1.86 + // grab the CGContextRef 1.87 + mCGContext = cairo_quartz_get_cg_context_with_clip(mSurfaceContext->GetCairo()); 1.88 + if (!mCGContext) 1.89 + return nullptr; 1.90 + 1.91 + gfxMatrix m = mContext->CurrentMatrix(); 1.92 + CGContextTranslateCTM(mCGContext, deviceOffset.x, deviceOffset.y); 1.93 + 1.94 + // I -think- that this context will always have an identity 1.95 + // transform (since we don't maintain a transform on it in 1.96 + // cairo-land, and instead push/pop as needed) 1.97 + 1.98 + gfxFloat x0 = m.x0; 1.99 + gfxFloat y0 = m.y0; 1.100 + 1.101 + // We round x0/y0 if we don't have a scale, because otherwise things get 1.102 + // rendered badly 1.103 + // XXX how should we be rounding x0/y0? 1.104 + if (!m.HasNonTranslationOrFlip()) { 1.105 + x0 = floor(x0 + 0.5); 1.106 + y0 = floor(y0 + 0.5); 1.107 + } 1.108 + 1.109 + CGContextConcatCTM(mCGContext, CGAffineTransformMake(m.xx, m.yx, 1.110 + m.xy, m.yy, 1.111 + x0, y0)); 1.112 + 1.113 + // bug 382049 - need to explicity set the composite operation to sourceOver 1.114 + CGContextSetCompositeOperation(mCGContext, kPrivateCGCompositeSourceOver); 1.115 + } else { 1.116 + nsIntSize backingSize(NSToIntFloor(mNativeRect.width * mBackingScale), 1.117 + NSToIntFloor(mNativeRect.height * mBackingScale)); 1.118 + mQuartzSurface = new gfxQuartzSurface(backingSize, 1.119 + gfxImageFormat::ARGB32); 1.120 + if (mQuartzSurface->CairoStatus()) 1.121 + return nullptr; 1.122 + mSurfaceContext = new gfxContext(mQuartzSurface); 1.123 + 1.124 + // grab the CGContextRef 1.125 + mCGContext = cairo_quartz_get_cg_context_with_clip(mSurfaceContext->GetCairo()); 1.126 + CGContextScaleCTM(mCGContext, mBackingScale, mBackingScale); 1.127 + CGContextTranslateCTM(mCGContext, -mNativeRect.X(), -mNativeRect.Y()); 1.128 + } 1.129 + 1.130 + return mCGContext; 1.131 +} 1.132 + 1.133 +void 1.134 +gfxQuartzNativeDrawing::EndNativeDrawing() 1.135 +{ 1.136 + NS_ASSERTION(mCGContext, "EndNativeDrawing called without BeginNativeDrawing"); 1.137 + 1.138 + if (mBorrowedContext.cg) { 1.139 + MOZ_ASSERT(!mContext->IsCairo()); 1.140 + mBorrowedContext.Finish(); 1.141 + if (mDrawTarget) { 1.142 + DrawTarget *dest = mContext->GetDrawTarget(); 1.143 + RefPtr<SourceSurface> source = mDrawTarget->Snapshot(); 1.144 + 1.145 + IntSize backingSize(NSToIntFloor(mNativeRect.width * mBackingScale), 1.146 + NSToIntFloor(mNativeRect.height * mBackingScale)); 1.147 + 1.148 + Matrix oldTransform = dest->GetTransform(); 1.149 + Matrix newTransform = oldTransform; 1.150 + newTransform.Translate(mNativeRect.x, mNativeRect.y); 1.151 + newTransform.Scale(1.0f / mBackingScale, 1.0f / mBackingScale); 1.152 + 1.153 + dest->SetTransform(newTransform); 1.154 + 1.155 + dest->DrawSurface(source, 1.156 + gfx::Rect(0, 0, backingSize.width, backingSize.height), 1.157 + gfx::Rect(0, 0, backingSize.width, backingSize.height)); 1.158 + 1.159 + 1.160 + dest->SetTransform(oldTransform); 1.161 + } 1.162 + return; 1.163 + } 1.164 + 1.165 + cairo_quartz_finish_cg_context_with_clip(mSurfaceContext->GetCairo()); 1.166 + mQuartzSurface->MarkDirty(); 1.167 + if (mSurfaceContext != mContext) { 1.168 + gfxContextMatrixAutoSaveRestore save(mContext); 1.169 + 1.170 + // Copy back to destination 1.171 + mContext->Translate(mNativeRect.TopLeft()); 1.172 + mContext->Scale(1.0f / mBackingScale, 1.0f / mBackingScale); 1.173 + mContext->DrawSurface(mQuartzSurface, mQuartzSurface->GetSize()); 1.174 + } 1.175 +}