From edf9be08c3683211fc04d3d285622691cd33f622 Mon Sep 17 00:00:00 2001 From: Tim Nguyen Date: Thu, 20 Feb 2020 23:50:06 +0000 Subject: [PATCH] Bug 1616587 - Implement conic-gradient for Skia graphics backend. r=lsalzman Differential Revision: https://phabricator.services.mozilla.com/D63415 --HG-- extra : moz-landing-system : lando --- gfx/2d/2D.h | 23 +++++++++++ gfx/2d/DrawCommands.h | 5 +++ gfx/2d/DrawTargetCairo.cpp | 14 +++++++ gfx/2d/DrawTargetRecording.cpp | 7 ++++ gfx/2d/DrawTargetSkia.cpp | 36 ++++++++++++++++++ gfx/2d/DrawTargetWrapAndRecord.cpp | 14 +++++++ gfx/2d/PatternHelpers.h | 14 +++++++ gfx/2d/RecordedEvent.h | 8 ++++ gfx/2d/RecordedEventImpl.h | 42 +++++++++++++++++++++ gfx/2d/Types.h | 3 +- gfx/thebes/gfxPattern.cpp | 16 +++++++- gfx/thebes/gfxPattern.h | 5 ++- layout/painting/nsCSSRenderingGradients.cpp | 5 +-- 13 files changed, 185 insertions(+), 7 deletions(-) diff --git a/gfx/2d/2D.h b/gfx/2d/2D.h index 6662129955f7..4e19aeac3c3f 100644 --- a/gfx/2d/2D.h +++ b/gfx/2d/2D.h @@ -290,6 +290,29 @@ class RadialGradientPattern : public Pattern { Matrix mMatrix; //!< A matrix that transforms the pattern into user space }; +/** + * This class is used for Conic Gradient Patterns, the gradient stops are + * stored in a separate object and are backend dependent. This class itself + * may be used on the stack. + */ +class ConicGradientPattern : public Pattern { + public: + /// For constructor parameter description, see member data documentation. + ConicGradientPattern(const Point& aCenter, Float aAngle, + GradientStops* aStops, const Matrix& aMatrix = Matrix()) + : mCenter(aCenter), mAngle(aAngle), mStops(aStops), mMatrix(aMatrix) {} + + PatternType GetType() const override { return PatternType::CONIC_GRADIENT; } + + Point mCenter; //!< Center of the gradient + Float mAngle; //!< Start angle of gradient + RefPtr + mStops; /**< GradientStops object for this gradient, this + should match the backend type of the draw target + this pattern will be used with. */ + Matrix mMatrix; //!< A matrix that transforms the pattern into user space +}; + /** * This class is used for Surface Patterns, they wrap a surface and a * repetition mode for the surface. This may be used on the stack. diff --git a/gfx/2d/DrawCommands.h b/gfx/2d/DrawCommands.h index eb7d1e8f1ecd..fd3f48346cb7 100644 --- a/gfx/2d/DrawCommands.h +++ b/gfx/2d/DrawCommands.h @@ -68,6 +68,10 @@ class StoredPattern { new (mRadial) RadialGradientPattern( *static_cast(&aPattern)); return; + case PatternType::CONIC_GRADIENT: + new (mConic) ConicGradientPattern( + *static_cast(&aPattern)); + return; } } @@ -98,6 +102,7 @@ class StoredPattern { char mColor[sizeof(ColorPattern)]; char mLinear[sizeof(LinearGradientPattern)]; char mRadial[sizeof(RadialGradientPattern)]; + char mConic[sizeof(ConicGradientPattern)]; char mSurface[sizeof(SurfacePattern)]; }; }; diff --git a/gfx/2d/DrawTargetCairo.cpp b/gfx/2d/DrawTargetCairo.cpp index e38b976f557a..5c57b261d2ae 100644 --- a/gfx/2d/DrawTargetCairo.cpp +++ b/gfx/2d/DrawTargetCairo.cpp @@ -184,6 +184,11 @@ static bool PatternIsCompatible(const Pattern& aPattern) { static_cast(aPattern); return pattern.mStops->GetBackendType() == BackendType::CAIRO; } + case PatternType::CONIC_GRADIENT: { + const ConicGradientPattern& pattern = + static_cast(aPattern); + return pattern.mStops->GetBackendType() == BackendType::CAIRO; + } default: return true; } @@ -521,6 +526,15 @@ static cairo_pattern_t* GfxPatternToCairoPattern(const Pattern& aPattern, break; } + case PatternType::CONIC_GRADIENT: { + const ConicGradientPattern& pattern = + static_cast(aPattern); + + // XXX(ntim): bug 1617039 - implement conic-gradient for Cairo + pat = cairo_pattern_create_rgba(0.0, 0.0, 0.0, 0.0); + + break; + } default: { // We should support all pattern types! MOZ_ASSERT(false); diff --git a/gfx/2d/DrawTargetRecording.cpp b/gfx/2d/DrawTargetRecording.cpp index ace2be55b3e6..8aede1bf8e59 100644 --- a/gfx/2d/DrawTargetRecording.cpp +++ b/gfx/2d/DrawTargetRecording.cpp @@ -633,6 +633,13 @@ void DrawTargetRecording::EnsurePatternDependenciesStored( static_cast(&aPattern)->mStops)); return; } + case PatternType::CONIC_GRADIENT: { + MOZ_ASSERT_IF( + static_cast(&aPattern)->mStops, + mRecorder->HasStoredObject( + static_cast(&aPattern)->mStops)); + return; + } case PatternType::SURFACE: { const SurfacePattern* pat = static_cast(&aPattern); EnsureSurfaceStoredRecording(mRecorder, pat->mSurface, diff --git a/gfx/2d/DrawTargetSkia.cpp b/gfx/2d/DrawTargetSkia.cpp index 563c55ad5714..f1c72a1b2418 100644 --- a/gfx/2d/DrawTargetSkia.cpp +++ b/gfx/2d/DrawTargetSkia.cpp @@ -494,6 +494,42 @@ static void SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, } break; } + case PatternType::CONIC_GRADIENT: { + const ConicGradientPattern& pat = + static_cast(aPattern); + GradientStopsSkia* stops = + static_cast(pat.mStops.get()); + if (!stops || stops->mCount < 2 || !pat.mCenter.IsFinite() || + !IsFinite(pat.mAngle)) { + aPaint.setColor(SK_ColorTRANSPARENT); + } else { + SkMatrix mat; + GfxMatrixToSkiaMatrix(pat.mMatrix, mat); + if (aMatrix) { + mat.postConcat(*aMatrix); + } + + SkScalar cx = SkFloatToScalar(pat.mCenter.x); + SkScalar cy = SkFloatToScalar(pat.mCenter.y); + + // Skia's sweep gradient angles are relative to the x-axis, not the + // y-axis. + Float angle = (pat.mAngle * 180.0 / M_PI) - 90.0; + if (angle != 0.0) { + mat.preRotate(angle, cx, cy); + } + + sk_sp shader = SkGradientShader::MakeSweep( + cx, cy, &stops->mColors.front(), &stops->mPositions.front(), + stops->mCount, 0, &mat); + if (shader) { + aPaint.setShader(shader); + } else { + aPaint.setColor(SK_ColorTRANSPARENT); + } + } + break; + } case PatternType::SURFACE: { const SurfacePattern& pat = static_cast(aPattern); sk_sp image = diff --git a/gfx/2d/DrawTargetWrapAndRecord.cpp b/gfx/2d/DrawTargetWrapAndRecord.cpp index c645e135a43a..81027ea88f1c 100644 --- a/gfx/2d/DrawTargetWrapAndRecord.cpp +++ b/gfx/2d/DrawTargetWrapAndRecord.cpp @@ -258,6 +258,14 @@ struct AdjustedPattern final { radGradPat->mMatrix); return mPattern; } + case PatternType::CONIC_GRADIENT: { + ConicGradientPattern* conGradPat = + static_cast(mOrigPattern); + mPattern = new (mConGradPat) ConicGradientPattern( + conGradPat->mCenter, conGradPat->mAngle, + GetGradientStops(conGradPat->mStops), conGradPat->mMatrix); + return mPattern; + } default: return new (mColPat) ColorPattern(Color()); } @@ -269,6 +277,7 @@ struct AdjustedPattern final { char mColPat[sizeof(ColorPattern)]; char mLinGradPat[sizeof(LinearGradientPattern)]; char mRadGradPat[sizeof(RadialGradientPattern)]; + char mConGradPat[sizeof(ConicGradientPattern)]; char mSurfPat[sizeof(SurfacePattern)]; }; @@ -717,6 +726,11 @@ void DrawTargetWrapAndRecord::EnsurePatternDependenciesStored( static_cast(&aPattern)->mStops)); return; } + case PatternType::CONIC_GRADIENT: { + MOZ_ASSERT(mRecorder->HasStoredObject( + static_cast(&aPattern)->mStops)); + return; + } case PatternType::SURFACE: { const SurfacePattern* pat = static_cast(&aPattern); EnsureSurfaceStored(mRecorder, pat->mSurface, diff --git a/gfx/2d/PatternHelpers.h b/gfx/2d/PatternHelpers.h index d7047df5d49e..e289bc3125b2 100644 --- a/gfx/2d/PatternHelpers.h +++ b/gfx/2d/PatternHelpers.h @@ -52,6 +52,10 @@ class GeneralPattern final { mPattern = new (mRadialGradientPattern.addr()) RadialGradientPattern( static_cast(aPattern)); break; + case PatternType::CONIC_GRADIENT: + mPattern = new (mConicGradientPattern.addr()) ConicGradientPattern( + static_cast(aPattern)); + break; case PatternType::SURFACE: mPattern = new (mSurfacePattern.addr()) SurfacePattern(static_cast(aPattern)); @@ -86,6 +90,15 @@ class GeneralPattern final { return mRadialGradientPattern.addr(); } + ConicGradientPattern* InitConicGradientPattern( + const Point& aCenter, Float aAngle, GradientStops* aStops, + const Matrix& aMatrix = Matrix()) { + MOZ_ASSERT(!mPattern); + mPattern = new (mConicGradientPattern.addr()) + ConicGradientPattern(aCenter, aAngle, aStops, aMatrix); + return mConicGradientPattern.addr(); + } + SurfacePattern* InitSurfacePattern( SourceSurface* aSourceSurface, ExtendMode aExtendMode, const Matrix& aMatrix = Matrix(), @@ -113,6 +126,7 @@ class GeneralPattern final { AlignedStorage2 mColorPattern; AlignedStorage2 mLinearGradientPattern; AlignedStorage2 mRadialGradientPattern; + AlignedStorage2 mConicGradientPattern; AlignedStorage2 mSurfacePattern; }; Pattern* mPattern = nullptr; diff --git a/gfx/2d/RecordedEvent.h b/gfx/2d/RecordedEvent.h index c031d9143f12..11583bc8b74d 100644 --- a/gfx/2d/RecordedEvent.h +++ b/gfx/2d/RecordedEvent.h @@ -129,6 +129,13 @@ struct RadialGradientPatternStorage { Matrix mMatrix; }; +struct ConicGradientPatternStorage { + Point mCenter; + Float mAngle; + ReferencePtr mStops; + Matrix mMatrix; +}; + struct SurfacePatternStorage { ExtendMode mExtend; SamplingFilter mSamplingFilter; @@ -144,6 +151,7 @@ struct PatternStorage { char mColor[sizeof(ColorPatternStorage)]; char mLinear[sizeof(LinearGradientPatternStorage)]; char mRadial[sizeof(RadialGradientPatternStorage)]; + char mConic[sizeof(ConicGradientPatternStorage)]; char mSurface[sizeof(SurfacePatternStorage)]; }; }; diff --git a/gfx/2d/RecordedEventImpl.h b/gfx/2d/RecordedEventImpl.h index 4b5b35061fa3..47c98f98ff73 100644 --- a/gfx/2d/RecordedEventImpl.h +++ b/gfx/2d/RecordedEventImpl.h @@ -1564,6 +1564,12 @@ void RecordedEvent::RecordPatternData(S& aStream, &aPattern.mStorage)); return; } + case PatternType::CONIC_GRADIENT: { + WriteElement(aStream, + *reinterpret_cast( + &aPattern.mStorage)); + return; + } case PatternType::SURFACE: { WriteElement(aStream, *reinterpret_cast( &aPattern.mStorage)); @@ -1596,6 +1602,11 @@ void RecordedEvent::ReadPatternData(S& aStream, &aPattern.mStorage)); return; } + case PatternType::CONIC_GRADIENT: { + ReadElement(aStream, *reinterpret_cast( + &aPattern.mStorage)); + return; + } case PatternType::SURFACE: { SurfacePatternStorage* sps = reinterpret_cast(&aPattern.mStorage); @@ -1661,6 +1672,18 @@ inline void RecordedEvent::StorePattern(PatternStorage& aDestination, store->mStops = pat->mStops.get(); return; } + case PatternType::CONIC_GRADIENT: { + ConicGradientPatternStorage* store = + reinterpret_cast( + &aDestination.mStorage); + const ConicGradientPattern* pat = + static_cast(&aSource); + store->mCenter = pat->mCenter; + store->mAngle = pat->mAngle; + store->mMatrix = pat->mMatrix; + store->mStops = pat->mStops.get(); + return; + } case PatternType::SURFACE: { SurfacePatternStorage* store = reinterpret_cast(&aDestination.mStorage); @@ -1804,6 +1827,14 @@ inline void RecordedEvent::OutputSimplePatternInfo( << store->mCenter2.y << ") Radius 2: " << store->mRadius2; return; } + case PatternType::CONIC_GRADIENT: { + const ConicGradientPatternStorage* store = + reinterpret_cast( + &aStorage.mStorage); + aOutput << "ConicGradient (Center: (" << store->mCenter.x << ", " + << store->mCenter.y << ") Angle: " << store->mAngle; + return; + } case PatternType::SURFACE: { const SurfacePatternStorage* store = reinterpret_cast(&aStorage.mStorage); @@ -2139,6 +2170,16 @@ struct GenericPattern { storage->mMatrix); return mPattern; } + case PatternType::CONIC_GRADIENT: { + ConicGradientPatternStorage* storage = + reinterpret_cast(&mStorage->mStorage); + mPattern = new (mConGradPat) ConicGradientPattern( + storage->mCenter, storage->mAngle, + storage->mStops ? mTranslator->LookupGradientStops(storage->mStops) + : nullptr, + storage->mMatrix); + return mPattern; + } default: return new (mColPat) ColorPattern(Color()); } @@ -2150,6 +2191,7 @@ struct GenericPattern { char mColPat[sizeof(ColorPattern)]; char mLinGradPat[sizeof(LinearGradientPattern)]; char mRadGradPat[sizeof(RadialGradientPattern)]; + char mConGradPat[sizeof(ConicGradientPattern)]; char mSurfPat[sizeof(SurfacePattern)]; }; diff --git a/gfx/2d/Types.h b/gfx/2d/Types.h index c6575f88decd..b64e686860ae 100644 --- a/gfx/2d/Types.h +++ b/gfx/2d/Types.h @@ -414,7 +414,8 @@ enum class PatternType : int8_t { COLOR, SURFACE, LINEAR_GRADIENT, - RADIAL_GRADIENT + RADIAL_GRADIENT, + CONIC_GRADIENT }; enum class JoinStyle : int8_t { diff --git a/gfx/thebes/gfxPattern.cpp b/gfx/thebes/gfxPattern.cpp index ee9e7cc870ee..86b6eacd6753 100644 --- a/gfx/thebes/gfxPattern.cpp +++ b/gfx/thebes/gfxPattern.cpp @@ -37,6 +37,12 @@ gfxPattern::gfxPattern(gfxFloat cx0, gfxFloat cy0, gfxFloat radius0, radius0, radius1, nullptr); } +// conic +gfxPattern::gfxPattern(gfxFloat cx, gfxFloat cy, gfxFloat angle) + : mExtend(ExtendMode::CLAMP) { + mGfxPattern.InitConicGradientPattern(Point(cx, cy), angle, nullptr); +} + // Azure gfxPattern::gfxPattern(SourceSurface* aSurface, const Matrix& aPatternToUserSpace) @@ -48,7 +54,8 @@ gfxPattern::gfxPattern(SourceSurface* aSurface, void gfxPattern::AddColorStop(gfxFloat offset, const Color& c) { if (mGfxPattern.GetPattern()->GetType() != PatternType::LINEAR_GRADIENT && - mGfxPattern.GetPattern()->GetType() != PatternType::RADIAL_GRADIENT) { + mGfxPattern.GetPattern()->GetType() != PatternType::RADIAL_GRADIENT && + mGfxPattern.GetPattern()->GetType() != PatternType::CONIC_GRADIENT) { return; } @@ -136,6 +143,13 @@ Pattern* gfxPattern::GetPattern(const DrawTarget* aTarget, radialGradientPattern->mStops = mStops; break; } + case PatternType::CONIC_GRADIENT: { + ConicGradientPattern* conicGradientPattern = + static_cast(mGfxPattern.GetPattern()); + conicGradientPattern->mMatrix = patternToUser; + conicGradientPattern->mStops = mStops; + break; + } default: /* Reassure the compiler we are handling all the enum values. */ break; diff --git a/gfx/thebes/gfxPattern.h b/gfx/thebes/gfxPattern.h index 68fde9791708..5eacb0ee2ee8 100644 --- a/gfx/thebes/gfxPattern.h +++ b/gfx/thebes/gfxPattern.h @@ -22,10 +22,11 @@ class gfxPattern final { public: explicit gfxPattern(const mozilla::gfx::Color& aColor); - // linear + // gradients gfxPattern(gfxFloat x0, gfxFloat y0, gfxFloat x1, gfxFloat y1); // linear gfxPattern(gfxFloat cx0, gfxFloat cy0, gfxFloat radius0, gfxFloat cx1, - gfxFloat cy1, gfxFloat radius1); // radial + gfxFloat cy1, gfxFloat radius1); // radial + gfxPattern(gfxFloat cx, gfxFloat cy, gfxFloat angle); // conic gfxPattern(mozilla::gfx::SourceSurface* aSurface, const mozilla::gfx::Matrix& aPatternToUserSpace); diff --git a/layout/painting/nsCSSRenderingGradients.cpp b/layout/painting/nsCSSRenderingGradients.cpp index a13834b502b8..288ac2ec6e59 100644 --- a/layout/painting/nsCSSRenderingGradients.cpp +++ b/layout/painting/nsCSSRenderingGradients.cpp @@ -951,8 +951,7 @@ void nsCSSGradientRenderer::Paint(gfxContext& aContext, const nsRect& aDest, matrix.PreTranslate(-mLineStart); } } else { - // conic-gradient is only implemented for WebRender - return; + gradientPattern = new gfxPattern(mCenter.x, mCenter.y, mAngle); } // Use a pattern transform to take account of source and dest rects matrix.PreTranslate(gfxPoint(mPresContext->CSSPixelsToDevPixels(aSrc.x), @@ -962,7 +961,7 @@ void nsCSSGradientRenderer::Paint(gfxContext& aContext, const nsRect& aDest, gfxFloat(nsPresContext::CSSPixelsToAppUnits(aSrc.height)) / aDest.height); gradientPattern->SetMatrix(matrix); - if (stopDelta == 0.0) { + if (!mGradient->IsConic() && stopDelta == 0.0) { // Non-repeating gradient with all stops in same place -> just add // first stop and last stop, both at position 0. // Repeating gradient with all stops in the same place, or radial