Bug 1160335. Make DrawTargetCG's transform handling better. r=mstange

This let's us avoid depending on Save() and Restore() to have the
right transform at the right time.
This commit is contained in:
Jeff Muizelaar 2015-04-30 17:53:25 -04:00
parent 8e254c0468
commit f8a6b0ef50
2 changed files with 51 additions and 74 deletions

View File

@ -275,16 +275,20 @@ class UnboundnessFixer
public:
UnboundnessFixer() : mLayerCg(nullptr) {}
CGContextRef Check(CGContextRef baseCg, CompositionOp blend, const Rect* maskBounds = nullptr)
CGContextRef Check(DrawTargetCG* dt, CompositionOp blend, const Rect* maskBounds = nullptr)
{
MOZ_ASSERT(baseCg);
MOZ_ASSERT(dt->mCg);
if (!IsOperatorBoundByMask(blend)) {
mClipBounds = CGContextGetClipBoundingBox(baseCg);
// The clip bounding box will be in user space so we need to clear our transform first
CGContextSetCTM(dt->mCg, dt->mOriginalTransform);
mClipBounds = CGContextGetClipBoundingBox(dt->mCg);
// If we're entirely clipped out or if the drawing operation covers the entire clip then
// we don't need to create a temporary surface.
if (CGRectIsEmpty(mClipBounds) ||
(maskBounds && maskBounds->Contains(CGRectToRect(mClipBounds)))) {
return baseCg;
CGContextConcatCTM(dt->mCg, GfxMatrixToCGAffineTransform(dt->mTransform));
return dt->mCg;
}
// TransparencyLayers aren't blended using the blend mode so
@ -292,7 +296,7 @@ class UnboundnessFixer
//XXX: The size here is in default user space units, of the layer relative to the graphics context.
// is the clip bounds still correct if, for example, we have a scale applied to the context?
mLayer = CGLayerCreateWithContext(baseCg, mClipBounds.size, nullptr);
mLayer = CGLayerCreateWithContext(dt->mCg, mClipBounds.size, nullptr);
mLayerCg = CGLayerGetContext(mLayer);
// CGContext's default to have the origin at the bottom left
// so flip it to the top left and adjust for the origin
@ -302,23 +306,27 @@ class UnboundnessFixer
}
CGContextTranslateCTM(mLayerCg, -mClipBounds.origin.x, mClipBounds.origin.y + mClipBounds.size.height);
CGContextScaleCTM(mLayerCg, 1, -1);
CGContextConcatCTM(mLayerCg, GfxMatrixToCGAffineTransform(dt->mTransform));
return mLayerCg;
} else {
return baseCg;
return dt->mCg;
}
}
void Fix(CGContextRef baseCg)
void Fix(DrawTargetCG *dt)
{
if (mLayerCg) {
// we pushed a layer so draw it to baseCg
MOZ_ASSERT(baseCg);
CGContextTranslateCTM(baseCg, 0, mClipBounds.size.height);
CGContextScaleCTM(baseCg, 1, -1);
// we pushed a layer so draw it to dt->mCg
MOZ_ASSERT(dt->mCg);
CGContextTranslateCTM(dt->mCg, 0, mClipBounds.size.height);
CGContextScaleCTM(dt->mCg, 1, -1);
mClipBounds.origin.y *= -1;
CGContextDrawLayerAtPoint(baseCg, mClipBounds.origin, mLayer);
CGContextDrawLayerAtPoint(dt->mCg, mClipBounds.origin, mLayer);
CGContextRelease(mLayerCg);
// Reset the transform
CGContextConcatCTM(dt->mCg, GfxMatrixToCGAffineTransform(dt->mTransform));
}
}
};
@ -339,15 +347,13 @@ DrawTargetCG::DrawSurface(SourceSurface *aSurface,
CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
UnboundnessFixer fixer;
CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp, &aDest);
CGContextRef cg = fixer.Check(this, aDrawOptions.mCompositionOp, &aDest);
if (MOZ2D_ERROR_IF(!cg)) {
return;
}
CGContextSetAlpha(cg, aDrawOptions.mAlpha);
CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
CGContextSetInterpolationQuality(cg, InterpolationQualityFromFilter(aSurfOptions.mFilter));
CGImageRef image = GetRetainedImageFromSourceSurface(aSurface);
@ -381,7 +387,7 @@ DrawTargetCG::DrawSurface(SourceSurface *aSurface,
CGImageRelease(image);
}
fixer.Fix(mCg);
fixer.Fix(this);
CGContextRestoreGState(mCg);
}
@ -894,7 +900,7 @@ DrawTargetCG::MaskSurface(const Pattern &aSource,
CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
UnboundnessFixer fixer;
CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
CGContextRef cg = fixer.Check(this, aDrawOptions.mCompositionOp);
if (MOZ2D_ERROR_IF(!cg)) {
return;
}
@ -902,7 +908,6 @@ DrawTargetCG::MaskSurface(const Pattern &aSource,
CGContextSetAlpha(cg, aDrawOptions.mAlpha);
CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
CGImageRef image = GetRetainedImageFromSourceSurface(aMask);
// use a negative-y so that the mask image draws right ways up
@ -924,12 +929,18 @@ DrawTargetCG::MaskSurface(const Pattern &aSource,
CGImageRelease(image);
fixer.Fix(mCg);
fixer.Fix(this);
CGContextRestoreGState(mCg);
}
void
DrawTargetCG::SetTransform(const Matrix &aTransform)
{
mTransform = aTransform;
CGContextSetCTM(mCg, mOriginalTransform);
CGContextConcatCTM(mCg, GfxMatrixToCGAffineTransform(aTransform));
}
void
DrawTargetCG::FillRect(const Rect &aRect,
@ -945,7 +956,7 @@ DrawTargetCG::FillRect(const Rect &aRect,
CGContextSaveGState(mCg);
UnboundnessFixer fixer;
CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp, &aRect);
CGContextRef cg = fixer.Check(this, aDrawOptions.mCompositionOp, &aRect);
if (MOZ2D_ERROR_IF(!cg)) {
return;
}
@ -954,8 +965,6 @@ DrawTargetCG::FillRect(const Rect &aRect,
CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
if (isGradient(aPattern)) {
CGContextClipToRect(cg, RectToCGRect(aRect));
CGRect clipBounds = CGContextGetClipBoundingBox(cg);
@ -989,7 +998,7 @@ DrawTargetCG::FillRect(const Rect &aRect,
CGContextFillRect(cg, RectToCGRect(aRect));
}
fixer.Fix(mCg);
fixer.Fix(this);
CGContextRestoreGState(mCg);
}
@ -1169,7 +1178,7 @@ DrawTargetCG::StrokeLine(const Point &aP1, const Point &aP2, const Pattern &aPat
CGContextSaveGState(mCg);
UnboundnessFixer fixer;
CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
CGContextRef cg = fixer.Check(this, aDrawOptions.mCompositionOp);
if (MOZ2D_ERROR_IF(!cg)) {
return;
}
@ -1177,8 +1186,6 @@ DrawTargetCG::StrokeLine(const Point &aP1, const Point &aP2, const Pattern &aPat
CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
CGContextBeginPath(cg);
CGContextMoveToPoint(cg, p1.x, p1.y);
CGContextAddLineToPoint(cg, p2.x, p2.y);
@ -1196,7 +1203,7 @@ DrawTargetCG::StrokeLine(const Point &aP1, const Point &aP2, const Pattern &aPat
CGContextStrokePath(cg);
}
fixer.Fix(mCg);
fixer.Fix(this);
CGContextRestoreGState(mCg);
}
@ -1247,7 +1254,7 @@ DrawTargetCG::StrokeRect(const Rect &aRect,
CGContextSaveGState(mCg);
UnboundnessFixer fixer;
CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
CGContextRef cg = fixer.Check(this, aDrawOptions.mCompositionOp);
if (MOZ2D_ERROR_IF(!cg)) {
return;
}
@ -1267,8 +1274,6 @@ DrawTargetCG::StrokeRect(const Rect &aRect,
CGContextSetShouldAntialias(cg,
aDrawOptions.mAntialiasMode != AntialiasMode::NONE && !pixelAlignedStroke);
CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
SetStrokeOptions(cg, aStrokeOptions);
if (isGradient(aPattern)) {
@ -1296,7 +1301,7 @@ DrawTargetCG::StrokeRect(const Rect &aRect,
CGContextStrokePath(cg);
}
fixer.Fix(mCg);
fixer.Fix(this);
CGContextRestoreGState(mCg);
}
@ -1311,7 +1316,6 @@ DrawTargetCG::ClearRect(const Rect &aRect)
MarkChanged();
CGContextSaveGState(mCg);
CGContextConcatCTM(mCg, GfxMatrixToCGAffineTransform(mTransform));
CGContextClearRect(mCg, RectToCGRect(aRect));
@ -1334,7 +1338,7 @@ DrawTargetCG::Stroke(const Path *aPath, const Pattern &aPattern, const StrokeOpt
CGContextSaveGState(mCg);
UnboundnessFixer fixer;
CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
CGContextRef cg = fixer.Check(this, aDrawOptions.mCompositionOp);
if (MOZ2D_ERROR_IF(!cg)) {
return;
}
@ -1343,8 +1347,6 @@ DrawTargetCG::Stroke(const Path *aPath, const Pattern &aPattern, const StrokeOpt
CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
CGContextBeginPath(cg);
@ -1367,7 +1369,7 @@ DrawTargetCG::Stroke(const Path *aPath, const Pattern &aPattern, const StrokeOpt
CGContextStrokePath(cg);
}
fixer.Fix(mCg);
fixer.Fix(this);
CGContextRestoreGState(mCg);
}
@ -1386,7 +1388,7 @@ DrawTargetCG::Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions
CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
UnboundnessFixer fixer;
CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
CGContextRef cg = fixer.Check(this, aDrawOptions.mCompositionOp);
if (MOZ2D_ERROR_IF(!cg)) {
return;
}
@ -1394,8 +1396,6 @@ DrawTargetCG::Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions
CGContextSetAlpha(cg, aDrawOptions.mAlpha);
CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
CGContextBeginPath(cg);
// XXX: we could put fill mode into the path fill rule if we wanted
const PathCG *cgPath = static_cast<const PathCG*>(aPath);
@ -1429,7 +1429,7 @@ DrawTargetCG::Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions
CGContextFillPath(cg);
}
fixer.Fix(mCg);
fixer.Fix(this);
CGContextRestoreGState(mCg);
}
@ -1515,7 +1515,7 @@ DrawTargetCG::FillGlyphs(ScaledFont *aFont, const GlyphBuffer &aBuffer, const Pa
CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
UnboundnessFixer fixer;
CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp);
CGContextRef cg = fixer.Check(this, aDrawOptions.mCompositionOp);
if (MOZ2D_ERROR_IF(!cg)) {
return;
}
@ -1526,8 +1526,6 @@ DrawTargetCG::FillGlyphs(ScaledFont *aFont, const GlyphBuffer &aBuffer, const Pa
CGContextSetShouldSmoothFonts(cg, aDrawOptions.mAntialiasMode == AntialiasMode::SUBPIXEL);
}
CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
ScaledFontMac* macFont = static_cast<ScaledFontMac*>(aFont);
// This code can execute millions of times in short periods, so we want to
@ -1597,7 +1595,7 @@ DrawTargetCG::FillGlyphs(ScaledFont *aFont, const GlyphBuffer &aBuffer, const Pa
}
}
fixer.Fix(mCg);
fixer.Fix(this);
CGContextRestoreGState(cg);
}
@ -1625,6 +1623,7 @@ DrawTargetCG::CopySurface(SourceSurface *aSurface,
// XXX: it might be more efficient for us to do the copy directly if we have access to the bits
CGContextSaveGState(mCg);
CGContextSetCTM(mCg, mOriginalTransform);
// CopySurface ignores the clip, so we need to use private API to temporarily reset it
CGContextResetClip(mCg);
@ -1664,6 +1663,7 @@ DrawTargetCG::DrawSurfaceWithShadow(SourceSurface *aSurface, const Point &aDest,
IntSize size = aSurface->GetSize();
CGContextSaveGState(mCg);
CGContextSetCTM(mCg, mOriginalTransform);
//XXX do we need to do the fixup here?
CGContextSetBlendMode(mCg, ToBlendMode(aOperator));
@ -1682,7 +1682,6 @@ DrawTargetCG::DrawSurfaceWithShadow(SourceSurface *aSurface, const Point &aDest,
CGImageRelease(image);
CGContextRestoreGState(mCg);
}
bool
@ -1774,6 +1773,7 @@ DrawTargetCG::Init(BackendType aType,
// so flip it to the top left
CGContextTranslateCTM(mCg, 0, mSize.height);
CGContextScaleCTM(mCg, 1, -1);
mOriginalTransform = CGContextGetCTM(mCg);
// See Bug 722164 for performance details
// Medium or higher quality lead to expensive interpolation
// for canvas we want to use low quality interpolation
@ -1879,6 +1879,7 @@ DrawTargetCG::Init(CGContextRef cgContext, const IntSize &aSize)
//
// CGContextTranslateCTM(mCg, 0, mSize.height);
// CGContextScaleCTM(mCg, 1, -1);
mOriginalTransform = CGContextGetCTM(mCg);
mFormat = SurfaceFormat::B8G8R8A8;
if (GetContextType(mCg) == CG_CONTEXT_TYPE_BITMAP) {
@ -1963,18 +1964,9 @@ DrawTargetCG::PushClipRect(const Rect &aRect)
return;
}
#ifdef DEBUG
mSavedClipBounds.push_back(CGContextGetClipBoundingBox(mCg));
#endif
CGContextSaveGState(mCg);
/* We go through a bit of trouble to temporarilly set the transform
* while we add the path */
CGAffineTransform previousTransform = CGContextGetCTM(mCg);
CGContextConcatCTM(mCg, GfxMatrixToCGAffineTransform(mTransform));
CGContextClipToRect(mCg, RectToCGRect(aRect));
CGContextSetCTM(mCg, previousTransform);
}
@ -1985,10 +1977,6 @@ DrawTargetCG::PushClip(const Path *aPath)
return;
}
#ifdef DEBUG
mSavedClipBounds.push_back(CGContextGetClipBoundingBox(mCg));
#endif
CGContextSaveGState(mCg);
CGContextBeginPath(mCg);
@ -2009,7 +1997,6 @@ DrawTargetCG::PushClip(const Path *aPath)
* while we add the path. XXX: this could be improved if we keep
* the CTM as resident state on the DrawTarget. */
CGContextSaveGState(mCg);
CGContextConcatCTM(mCg, GfxMatrixToCGAffineTransform(mTransform));
CGContextAddPath(mCg, cgPath->GetPath());
CGContextRestoreGState(mCg);
@ -2023,13 +2010,6 @@ void
DrawTargetCG::PopClip()
{
CGContextRestoreGState(mCg);
#ifdef DEBUG
MOZ_ASSERT(!mSavedClipBounds.empty(), "Unbalanced PopClip");
MOZ_ASSERT(CGRectEqualToRect(mSavedClipBounds.back(), CGContextGetClipBoundingBox(mCg)),
"PopClip didn't restore original clip");
mSavedClipBounds.pop_back();
#endif
}
void
@ -2064,8 +2044,6 @@ BorrowedCGContext::BorrowCGContextFromDrawTarget(DrawTarget *aDT)
// 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;

View File

@ -19,7 +19,7 @@ namespace mozilla {
namespace gfx {
static inline CGAffineTransform
GfxMatrixToCGAffineTransform(Matrix m)
GfxMatrixToCGAffineTransform(const Matrix &m)
{
CGAffineTransform t;
t.a = m._11;
@ -113,6 +113,7 @@ class DrawTargetCG : public DrawTarget
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetCG, override)
friend class BorrowedCGContext;
friend class UnboundnessFixer;
friend class SourceSurfaceCGBitmapContext;
DrawTargetCG();
virtual ~DrawTargetCG();
@ -173,6 +174,7 @@ public:
virtual IntSize GetSize() override { return mSize; }
virtual void SetTransform(const Matrix &aTransform) override;
/* This is for creating good compatible surfaces */
virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
@ -196,6 +198,7 @@ private:
IntSize mSize;
CGColorSpaceRef mColorSpace;
CGContextRef mCg;
CGAffineTransform mOriginalTransform;
/**
* The image buffer, if the buffer is owned by this class.
@ -207,10 +210,6 @@ private:
RefPtr<SourceSurfaceCGContext> mSnapshot;
bool mMayContainInvalidPremultipliedData;
#ifdef DEBUG
std::vector<CGRect> mSavedClipBounds;
#endif
};
}