Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "gfxQuartzNativeDrawing.h"
7 #include "gfxQuartzSurface.h"
8 #include "gfxPlatform.h"
9 #include "cairo-quartz.h"
11 // see cairo-quartz-surface.c for the complete list of these
12 enum {
13 kPrivateCGCompositeSourceOver = 2
14 };
16 using namespace mozilla::gfx;
17 using namespace mozilla;
19 // private Quartz routine needed here
20 extern "C" {
21 CG_EXTERN void CGContextSetCompositeOperation(CGContextRef, int);
22 }
24 gfxQuartzNativeDrawing::gfxQuartzNativeDrawing(gfxContext* ctx,
25 const gfxRect& nativeRect,
26 gfxFloat aBackingScale)
27 : mContext(ctx)
28 , mNativeRect(nativeRect)
29 , mBackingScale(aBackingScale)
30 {
31 mNativeRect.RoundOut();
32 }
34 CGContextRef
35 gfxQuartzNativeDrawing::BeginNativeDrawing()
36 {
37 NS_ASSERTION(!mQuartzSurface, "BeginNativeDrawing called when drawing already in progress");
39 gfxPoint deviceOffset;
40 nsRefPtr<gfxASurface> surf;
42 if (!mContext->IsCairo()) {
43 DrawTarget *dt = mContext->GetDrawTarget();
44 if (dt->GetType() == BackendType::COREGRAPHICS) {
45 if (dt->IsDualDrawTarget()) {
46 IntSize backingSize(NSToIntFloor(mNativeRect.width * mBackingScale),
47 NSToIntFloor(mNativeRect.height * mBackingScale));
49 if (backingSize.IsEmpty())
50 return nullptr;
52 mDrawTarget = Factory::CreateDrawTarget(BackendType::COREGRAPHICS, backingSize, SurfaceFormat::B8G8R8A8);
54 Matrix transform;
55 transform.Scale(mBackingScale, mBackingScale);
56 transform.Translate(-mNativeRect.x, -mNativeRect.y);
58 mDrawTarget->SetTransform(transform);
59 dt = mDrawTarget;
60 }
62 mCGContext = mBorrowedContext.Init(dt);
63 MOZ_ASSERT(mCGContext);
64 return mCGContext;
65 }
66 surf = gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(dt);
67 } else {
68 surf = mContext->CurrentSurface(&deviceOffset.x, &deviceOffset.y);
69 }
70 if (!surf || surf->CairoStatus())
71 return nullptr;
73 // if this is a native Quartz surface, we don't have to redirect
74 // rendering to our own CGContextRef; in most cases, we are able to
75 // use the CGContextRef from the surface directly. we can extend
76 // this to support offscreen drawing fairly easily in the future.
77 if (surf->GetType() == gfxSurfaceType::Quartz &&
78 (surf->GetContentType() == gfxContentType::COLOR ||
79 (surf->GetContentType() == gfxContentType::COLOR_ALPHA))) {
80 mQuartzSurface = static_cast<gfxQuartzSurface*>(surf.get());
81 mSurfaceContext = mContext;
83 // grab the CGContextRef
84 mCGContext = cairo_quartz_get_cg_context_with_clip(mSurfaceContext->GetCairo());
85 if (!mCGContext)
86 return nullptr;
88 gfxMatrix m = mContext->CurrentMatrix();
89 CGContextTranslateCTM(mCGContext, deviceOffset.x, deviceOffset.y);
91 // I -think- that this context will always have an identity
92 // transform (since we don't maintain a transform on it in
93 // cairo-land, and instead push/pop as needed)
95 gfxFloat x0 = m.x0;
96 gfxFloat y0 = m.y0;
98 // We round x0/y0 if we don't have a scale, because otherwise things get
99 // rendered badly
100 // XXX how should we be rounding x0/y0?
101 if (!m.HasNonTranslationOrFlip()) {
102 x0 = floor(x0 + 0.5);
103 y0 = floor(y0 + 0.5);
104 }
106 CGContextConcatCTM(mCGContext, CGAffineTransformMake(m.xx, m.yx,
107 m.xy, m.yy,
108 x0, y0));
110 // bug 382049 - need to explicity set the composite operation to sourceOver
111 CGContextSetCompositeOperation(mCGContext, kPrivateCGCompositeSourceOver);
112 } else {
113 nsIntSize backingSize(NSToIntFloor(mNativeRect.width * mBackingScale),
114 NSToIntFloor(mNativeRect.height * mBackingScale));
115 mQuartzSurface = new gfxQuartzSurface(backingSize,
116 gfxImageFormat::ARGB32);
117 if (mQuartzSurface->CairoStatus())
118 return nullptr;
119 mSurfaceContext = new gfxContext(mQuartzSurface);
121 // grab the CGContextRef
122 mCGContext = cairo_quartz_get_cg_context_with_clip(mSurfaceContext->GetCairo());
123 CGContextScaleCTM(mCGContext, mBackingScale, mBackingScale);
124 CGContextTranslateCTM(mCGContext, -mNativeRect.X(), -mNativeRect.Y());
125 }
127 return mCGContext;
128 }
130 void
131 gfxQuartzNativeDrawing::EndNativeDrawing()
132 {
133 NS_ASSERTION(mCGContext, "EndNativeDrawing called without BeginNativeDrawing");
135 if (mBorrowedContext.cg) {
136 MOZ_ASSERT(!mContext->IsCairo());
137 mBorrowedContext.Finish();
138 if (mDrawTarget) {
139 DrawTarget *dest = mContext->GetDrawTarget();
140 RefPtr<SourceSurface> source = mDrawTarget->Snapshot();
142 IntSize backingSize(NSToIntFloor(mNativeRect.width * mBackingScale),
143 NSToIntFloor(mNativeRect.height * mBackingScale));
145 Matrix oldTransform = dest->GetTransform();
146 Matrix newTransform = oldTransform;
147 newTransform.Translate(mNativeRect.x, mNativeRect.y);
148 newTransform.Scale(1.0f / mBackingScale, 1.0f / mBackingScale);
150 dest->SetTransform(newTransform);
152 dest->DrawSurface(source,
153 gfx::Rect(0, 0, backingSize.width, backingSize.height),
154 gfx::Rect(0, 0, backingSize.width, backingSize.height));
157 dest->SetTransform(oldTransform);
158 }
159 return;
160 }
162 cairo_quartz_finish_cg_context_with_clip(mSurfaceContext->GetCairo());
163 mQuartzSurface->MarkDirty();
164 if (mSurfaceContext != mContext) {
165 gfxContextMatrixAutoSaveRestore save(mContext);
167 // Copy back to destination
168 mContext->Translate(mNativeRect.TopLeft());
169 mContext->Scale(1.0f / mBackingScale, 1.0f / mBackingScale);
170 mContext->DrawSurface(mQuartzSurface, mQuartzSurface->GetSize());
171 }
172 }