From 15772d63873529e9d1129508d1bed2e7b140e1d8 Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Thu, 30 Apr 2015 17:53:25 -0400 Subject: [PATCH] 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. --HG-- extra : rebase_source : 81a64c70683d4b91b519fcb5eb8296ffa3e61e2a --- gfx/2d/DrawTargetCG.cpp | 116 ++++++++++++++++------------------------ gfx/2d/DrawTargetCG.h | 9 ++-- 2 files changed, 51 insertions(+), 74 deletions(-) diff --git a/gfx/2d/DrawTargetCG.cpp b/gfx/2d/DrawTargetCG.cpp index 80686acc8a67..a0108107ffe7 100644 --- a/gfx/2d/DrawTargetCG.cpp +++ b/gfx/2d/DrawTargetCG.cpp @@ -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(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(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; diff --git a/gfx/2d/DrawTargetCG.h b/gfx/2d/DrawTargetCG.h index 44a9bd7a7592..a32713c2fc4c 100644 --- a/gfx/2d/DrawTargetCG.h +++ b/gfx/2d/DrawTargetCG.h @@ -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 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 mSnapshot; bool mMayContainInvalidPremultipliedData; - -#ifdef DEBUG - std::vector mSavedClipBounds; -#endif }; }