CSSClipPathInstance.cpp (8829B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 // Main header first: 8 #include "CSSClipPathInstance.h" 9 10 #include "gfx2DGlue.h" 11 #include "gfxContext.h" 12 #include "gfxPlatform.h" 13 #include "mozilla/SVGUtils.h" 14 #include "mozilla/ShapeUtils.h" 15 #include "mozilla/dom/SVGElement.h" 16 #include "mozilla/dom/SVGPathData.h" 17 #include "mozilla/gfx/2D.h" 18 #include "mozilla/gfx/PathHelpers.h" 19 #include "nsIFrame.h" 20 #include "nsLayoutUtils.h" 21 22 using namespace mozilla::dom; 23 using namespace mozilla::gfx; 24 25 namespace mozilla { 26 27 /* static*/ 28 void CSSClipPathInstance::ApplyBasicShapeOrPathClip( 29 gfxContext& aContext, nsIFrame* aFrame, const gfxMatrix& aTransform) { 30 RefPtr<Path> path = 31 CreateClipPathForFrame(aContext.GetDrawTarget(), aFrame, aTransform); 32 if (!path) { 33 // This behavior matches |SVGClipPathFrame::ApplyClipPath()|. 34 // https://www.w3.org/TR/css-masking-1/#ClipPathElement: 35 // "An empty clipping path will completely clip away the element that had 36 // the clip-path property applied." 37 aContext.Clip(Rect()); 38 return; 39 } 40 aContext.Clip(path); 41 } 42 43 /* static*/ 44 RefPtr<Path> CSSClipPathInstance::CreateClipPathForFrame( 45 gfx::DrawTarget* aDt, nsIFrame* aFrame, const gfxMatrix& aTransform) { 46 const auto& clipPathStyle = aFrame->StyleSVGReset()->mClipPath; 47 MOZ_ASSERT(clipPathStyle.IsShape() || clipPathStyle.IsBox(), 48 "This is used with basic-shape, and geometry-box only"); 49 50 CSSClipPathInstance instance(aFrame, clipPathStyle); 51 52 return instance.CreateClipPath(aDt, aTransform); 53 } 54 55 /* static*/ 56 bool CSSClipPathInstance::HitTestBasicShapeOrPathClip(nsIFrame* aFrame, 57 const gfxPoint& aPoint) { 58 const auto& clipPathStyle = aFrame->StyleSVGReset()->mClipPath; 59 MOZ_ASSERT(!clipPathStyle.IsNone(), "unexpected none value"); 60 MOZ_ASSERT(!clipPathStyle.IsUrl(), "unexpected url value"); 61 62 CSSClipPathInstance instance(aFrame, clipPathStyle); 63 64 RefPtr<DrawTarget> drawTarget = 65 gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget(); 66 RefPtr<Path> path = instance.CreateClipPath( 67 drawTarget, SVGUtils::GetCSSPxToDevPxMatrix(aFrame)); 68 float pixelRatio = float(AppUnitsPerCSSPixel()) / 69 float(aFrame->PresContext()->AppUnitsPerDevPixel()); 70 return path && path->ContainsPoint(ToPoint(aPoint) * pixelRatio, Matrix()); 71 } 72 73 /* static */ 74 Maybe<Rect> CSSClipPathInstance::GetBoundingRectForBasicShapeOrPathClip( 75 nsIFrame* aFrame, const StyleClipPath& aClipPathStyle) { 76 MOZ_ASSERT(aClipPathStyle.IsShape() || aClipPathStyle.IsBox()); 77 78 CSSClipPathInstance instance(aFrame, aClipPathStyle); 79 80 RefPtr<DrawTarget> drawTarget = 81 gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget(); 82 RefPtr<Path> path = instance.CreateClipPath( 83 drawTarget, SVGUtils::GetCSSPxToDevPxMatrix(aFrame)); 84 return path ? Some(path->GetBounds()) : Nothing(); 85 } 86 87 already_AddRefed<Path> CSSClipPathInstance::CreateClipPath( 88 DrawTarget* aDrawTarget, const gfxMatrix& aTransform) { 89 nscoord appUnitsPerDevPixel = 90 mTargetFrame->PresContext()->AppUnitsPerDevPixel(); 91 92 nsRect r = nsLayoutUtils::ComputeClipPathGeometryBox( 93 mTargetFrame, mClipPathStyle.IsBox() ? mClipPathStyle.AsBox() 94 : mClipPathStyle.AsShape()._1); 95 96 gfxRect rr(r.x, r.y, r.width, r.height); 97 rr.Scale(1.0 / AppUnitsPerCSSPixel()); 98 rr = aTransform.TransformRect(rr); 99 rr.Scale(appUnitsPerDevPixel); 100 rr.Round(); 101 102 r = nsRect(int(rr.x), int(rr.y), int(rr.width), int(rr.height)); 103 104 if (mClipPathStyle.IsBox()) { 105 RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder(); 106 AppendRectToPath(builder, NSRectToRect(r, appUnitsPerDevPixel), true); 107 return builder->Finish(); 108 } 109 110 MOZ_ASSERT(mClipPathStyle.IsShape()); 111 112 r = ToAppUnits(r.ToNearestPixels(appUnitsPerDevPixel), appUnitsPerDevPixel); 113 114 const auto& basicShape = *mClipPathStyle.AsShape()._0; 115 switch (basicShape.tag) { 116 case StyleBasicShape::Tag::Circle: 117 return CreateClipPathCircle(aDrawTarget, r); 118 case StyleBasicShape::Tag::Ellipse: 119 return CreateClipPathEllipse(aDrawTarget, r); 120 case StyleBasicShape::Tag::Polygon: 121 return CreateClipPathPolygon(aDrawTarget, r); 122 case StyleBasicShape::Tag::Rect: 123 return CreateClipPathInset(aDrawTarget, r); 124 case StyleBasicShape::Tag::PathOrShape: 125 return basicShape.AsPathOrShape().IsPath() 126 ? CreateClipPathPath(aDrawTarget, r) 127 : CreateClipPathShape(aDrawTarget, r); 128 default: 129 MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected shape type"); 130 } 131 // Return an empty Path: 132 RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder(); 133 return builder->Finish(); 134 } 135 136 already_AddRefed<Path> CSSClipPathInstance::CreateClipPathCircle( 137 DrawTarget* aDrawTarget, const nsRect& aRefBox) { 138 const StyleBasicShape& shape = *mClipPathStyle.AsShape()._0; 139 const nsPoint& center = 140 ShapeUtils::ComputeCircleOrEllipseCenter(shape, aRefBox); 141 RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder(); 142 return ShapeUtils::BuildCirclePath( 143 shape, aRefBox, center, 144 mTargetFrame->PresContext()->AppUnitsPerDevPixel(), builder); 145 } 146 147 already_AddRefed<Path> CSSClipPathInstance::CreateClipPathEllipse( 148 DrawTarget* aDrawTarget, const nsRect& aRefBox) { 149 const StyleBasicShape& shape = *mClipPathStyle.AsShape()._0; 150 const nsPoint& center = 151 ShapeUtils::ComputeCircleOrEllipseCenter(shape, aRefBox); 152 RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder(); 153 return ShapeUtils::BuildEllipsePath( 154 shape, aRefBox, center, 155 mTargetFrame->PresContext()->AppUnitsPerDevPixel(), builder); 156 } 157 158 already_AddRefed<Path> CSSClipPathInstance::CreateClipPathPolygon( 159 DrawTarget* aDrawTarget, const nsRect& aRefBox) { 160 const auto& basicShape = *mClipPathStyle.AsShape()._0; 161 auto fillRule = basicShape.AsPolygon().fill == StyleFillRule::Nonzero 162 ? FillRule::FILL_WINDING 163 : FillRule::FILL_EVEN_ODD; 164 RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder(fillRule); 165 return ShapeUtils::BuildPolygonPath( 166 basicShape, aRefBox, mTargetFrame->PresContext()->AppUnitsPerDevPixel(), 167 builder); 168 } 169 170 already_AddRefed<Path> CSSClipPathInstance::CreateClipPathInset( 171 DrawTarget* aDrawTarget, const nsRect& aRefBox) { 172 RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder(); 173 return ShapeUtils::BuildInsetPath( 174 *mClipPathStyle.AsShape()._0, aRefBox, 175 mTargetFrame->PresContext()->AppUnitsPerDevPixel(), builder); 176 } 177 178 already_AddRefed<Path> CSSClipPathInstance::CreateClipPathPath( 179 DrawTarget* aDrawTarget, const nsRect& aRefBox) { 180 const auto& path = mClipPathStyle.AsShape()._0->AsPathOrShape().AsPath(); 181 182 RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder( 183 path.fill == StyleFillRule::Nonzero ? FillRule::FILL_WINDING 184 : FillRule::FILL_EVEN_ODD); 185 const nscoord appUnitsPerDevPixel = 186 mTargetFrame->PresContext()->AppUnitsPerDevPixel(); 187 const Point offset = 188 LayoutDevicePoint::FromAppUnits(aRefBox.TopLeft(), appUnitsPerDevPixel) 189 .ToUnknownPoint(); 190 const float scale = mTargetFrame->Style()->EffectiveZoom().Zoom( 191 float(AppUnitsPerCSSPixel()) / float(appUnitsPerDevPixel)); 192 return SVGPathData::BuildPath(path.path._0.AsSpan(), builder, 193 StyleStrokeLinecap::Butt, 0.0, {}, offset, 194 scale); 195 } 196 197 already_AddRefed<Path> CSSClipPathInstance::CreateClipPathShape( 198 DrawTarget* aDrawTarget, const nsRect& aRefBox) { 199 const auto& shape = mClipPathStyle.AsShape()._0->AsPathOrShape().AsShape(); 200 201 RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder( 202 shape.fill == StyleFillRule::Nonzero ? FillRule::FILL_WINDING 203 : FillRule::FILL_EVEN_ODD); 204 const nscoord appUnitsPerDevPixel = 205 mTargetFrame->PresContext()->AppUnitsPerDevPixel(); 206 const CSSSize basis = CSSSize::FromAppUnits(aRefBox.Size()); 207 const Point offset = 208 LayoutDevicePoint::FromAppUnits(aRefBox.TopLeft(), appUnitsPerDevPixel) 209 .ToUnknownPoint(); 210 const float scale = mTargetFrame->Style()->EffectiveZoom().Zoom( 211 float(AppUnitsPerCSSPixel()) / float(appUnitsPerDevPixel)); 212 return SVGPathData::BuildPath(shape.commands.AsSpan(), builder, 213 StyleStrokeLinecap::Butt, 0.0, basis, offset, 214 scale); 215 } 216 217 } // namespace mozilla