forked from mirrors/gecko-dev
		
	 988bc29033
			
		
	
	
		988bc29033
		
	
	
	
	
		
			
			Matches other browsers, and the spec, as per https://drafts.csswg.org/css-shapes/#basic-shape: All the lengths expressed in percentages are resolved from the used dimensions of the reference box. Differential Revision: https://phabricator.services.mozilla.com/D111790
		
			
				
	
	
		
			237 lines
		
	
	
	
		
			8.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			237 lines
		
	
	
	
		
			8.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| // Main header first:
 | |
| #include "CSSClipPathInstance.h"
 | |
| 
 | |
| #include "mozilla/dom/SVGElement.h"
 | |
| #include "mozilla/dom/SVGPathData.h"
 | |
| #include "mozilla/gfx/2D.h"
 | |
| #include "mozilla/gfx/PathHelpers.h"
 | |
| #include "mozilla/ShapeUtils.h"
 | |
| #include "mozilla/SVGUtils.h"
 | |
| #include "gfx2DGlue.h"
 | |
| #include "gfxContext.h"
 | |
| #include "gfxPlatform.h"
 | |
| #include "nsCSSRendering.h"
 | |
| #include "nsIFrame.h"
 | |
| #include "nsLayoutUtils.h"
 | |
| 
 | |
| using namespace mozilla::dom;
 | |
| using namespace mozilla::gfx;
 | |
| 
 | |
| namespace mozilla {
 | |
| 
 | |
| /* static*/
 | |
| void CSSClipPathInstance::ApplyBasicShapeOrPathClip(
 | |
|     gfxContext& aContext, nsIFrame* aFrame, const gfxMatrix& aTransform) {
 | |
|   const auto& clipPathStyle = aFrame->StyleSVGReset()->mClipPath;
 | |
|   MOZ_ASSERT(clipPathStyle.IsShape() || clipPathStyle.IsBox() ||
 | |
|                  clipPathStyle.IsPath(),
 | |
|              "This is used with basic-shape, geometry-box, and path() only");
 | |
| 
 | |
|   CSSClipPathInstance instance(aFrame, clipPathStyle);
 | |
| 
 | |
|   aContext.NewPath();
 | |
|   RefPtr<Path> path =
 | |
|       instance.CreateClipPath(aContext.GetDrawTarget(), aTransform);
 | |
|   if (!path) {
 | |
|     return;
 | |
|   }
 | |
|   aContext.SetPath(path);
 | |
|   aContext.Clip();
 | |
| }
 | |
| 
 | |
| /* static*/
 | |
| bool CSSClipPathInstance::HitTestBasicShapeOrPathClip(nsIFrame* aFrame,
 | |
|                                                       const gfxPoint& aPoint) {
 | |
|   const auto& clipPathStyle = aFrame->StyleSVGReset()->mClipPath;
 | |
|   MOZ_ASSERT(!clipPathStyle.IsNone(), "unexpected none value");
 | |
|   // In the future CSSClipPathInstance may handle <clipPath> references as
 | |
|   // well. For the time being return early.
 | |
|   if (clipPathStyle.IsUrl()) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   CSSClipPathInstance instance(aFrame, clipPathStyle);
 | |
| 
 | |
|   RefPtr<DrawTarget> drawTarget =
 | |
|       gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
 | |
|   RefPtr<Path> path = instance.CreateClipPath(
 | |
|       drawTarget, SVGUtils::GetCSSPxToDevPxMatrix(aFrame));
 | |
|   float pixelRatio = float(AppUnitsPerCSSPixel()) /
 | |
|                      aFrame->PresContext()->AppUnitsPerDevPixel();
 | |
|   return path && path->ContainsPoint(ToPoint(aPoint) * pixelRatio, Matrix());
 | |
| }
 | |
| 
 | |
| /* static */
 | |
| Maybe<Rect> CSSClipPathInstance::GetBoundingRectForBasicShapeOrPathClip(
 | |
|     nsIFrame* aFrame, const StyleClipPath& aClipPathStyle) {
 | |
|   MOZ_ASSERT(aClipPathStyle.IsShape() || aClipPathStyle.IsBox() ||
 | |
|              aClipPathStyle.IsPath());
 | |
| 
 | |
|   CSSClipPathInstance instance(aFrame, aClipPathStyle);
 | |
| 
 | |
|   RefPtr<DrawTarget> drawTarget =
 | |
|       gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
 | |
|   RefPtr<Path> path = instance.CreateClipPath(
 | |
|       drawTarget, SVGUtils::GetCSSPxToDevPxMatrix(aFrame));
 | |
|   return path ? Some(path->GetBounds()) : Nothing();
 | |
| }
 | |
| 
 | |
| already_AddRefed<Path> CSSClipPathInstance::CreateClipPath(
 | |
|     DrawTarget* aDrawTarget, const gfxMatrix& aTransform) {
 | |
|   if (mClipPathStyle.IsPath()) {
 | |
|     return CreateClipPathPath(aDrawTarget);
 | |
|   }
 | |
| 
 | |
|   nscoord appUnitsPerDevPixel =
 | |
|       mTargetFrame->PresContext()->AppUnitsPerDevPixel();
 | |
| 
 | |
|   nsRect r;
 | |
|   if (mClipPathStyle.IsBox()) {
 | |
|     r = nsLayoutUtils::ComputeGeometryBox(mTargetFrame, mClipPathStyle.AsBox());
 | |
|   } else {
 | |
|     r = nsLayoutUtils::ComputeGeometryBox(mTargetFrame,
 | |
|                                           mClipPathStyle.AsShape()._1);
 | |
|   }
 | |
| 
 | |
|   gfxRect rr(r.x, r.y, r.width, r.height);
 | |
|   rr.Scale(1.0 / AppUnitsPerCSSPixel());
 | |
|   rr = aTransform.TransformRect(rr);
 | |
|   rr.Scale(appUnitsPerDevPixel);
 | |
|   rr.Round();
 | |
| 
 | |
|   r = nsRect(int(rr.x), int(rr.y), int(rr.width), int(rr.height));
 | |
| 
 | |
|   if (mClipPathStyle.IsBox()) {
 | |
|     RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
 | |
|     AppendRectToPath(builder, NSRectToRect(r, appUnitsPerDevPixel), true);
 | |
|     return builder->Finish();
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT(mClipPathStyle.IsShape());
 | |
| 
 | |
|   r = ToAppUnits(r.ToNearestPixels(appUnitsPerDevPixel), appUnitsPerDevPixel);
 | |
| 
 | |
|   const auto& basicShape = *mClipPathStyle.AsShape()._0;
 | |
|   switch (basicShape.tag) {
 | |
|     case StyleBasicShape::Tag::Circle:
 | |
|       return CreateClipPathCircle(aDrawTarget, r);
 | |
|     case StyleBasicShape::Tag::Ellipse:
 | |
|       return CreateClipPathEllipse(aDrawTarget, r);
 | |
|     case StyleBasicShape::Tag::Polygon:
 | |
|       return CreateClipPathPolygon(aDrawTarget, r);
 | |
|     case StyleBasicShape::Tag::Inset:
 | |
|       return CreateClipPathInset(aDrawTarget, r);
 | |
|       break;
 | |
|     default:
 | |
|       MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected shape type");
 | |
|   }
 | |
|   // Return an empty Path:
 | |
|   RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
 | |
|   return builder->Finish();
 | |
| }
 | |
| 
 | |
| already_AddRefed<Path> CSSClipPathInstance::CreateClipPathCircle(
 | |
|     DrawTarget* aDrawTarget, const nsRect& aRefBox) {
 | |
|   const auto& basicShape = *mClipPathStyle.AsShape()._0;
 | |
| 
 | |
|   RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
 | |
| 
 | |
|   nsPoint center =
 | |
|       ShapeUtils::ComputeCircleOrEllipseCenter(basicShape, aRefBox);
 | |
|   nscoord r = ShapeUtils::ComputeCircleRadius(basicShape, center, aRefBox);
 | |
|   nscoord appUnitsPerDevPixel =
 | |
|       mTargetFrame->PresContext()->AppUnitsPerDevPixel();
 | |
|   builder->Arc(Point(center.x, center.y) / appUnitsPerDevPixel,
 | |
|                r / appUnitsPerDevPixel, 0, Float(2 * M_PI));
 | |
|   builder->Close();
 | |
|   return builder->Finish();
 | |
| }
 | |
| 
 | |
| already_AddRefed<Path> CSSClipPathInstance::CreateClipPathEllipse(
 | |
|     DrawTarget* aDrawTarget, const nsRect& aRefBox) {
 | |
|   const auto& basicShape = *mClipPathStyle.AsShape()._0;
 | |
| 
 | |
|   RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
 | |
| 
 | |
|   nsPoint center =
 | |
|       ShapeUtils::ComputeCircleOrEllipseCenter(basicShape, aRefBox);
 | |
|   nsSize radii = ShapeUtils::ComputeEllipseRadii(basicShape, center, aRefBox);
 | |
|   nscoord appUnitsPerDevPixel =
 | |
|       mTargetFrame->PresContext()->AppUnitsPerDevPixel();
 | |
|   EllipseToBezier(builder.get(),
 | |
|                   Point(center.x, center.y) / appUnitsPerDevPixel,
 | |
|                   Size(radii.width, radii.height) / appUnitsPerDevPixel);
 | |
|   builder->Close();
 | |
|   return builder->Finish();
 | |
| }
 | |
| 
 | |
| already_AddRefed<Path> CSSClipPathInstance::CreateClipPathPolygon(
 | |
|     DrawTarget* aDrawTarget, const nsRect& aRefBox) {
 | |
|   const auto& basicShape = *mClipPathStyle.AsShape()._0;
 | |
|   auto fillRule = basicShape.AsPolygon().fill == StyleFillRule::Nonzero
 | |
|                       ? FillRule::FILL_WINDING
 | |
|                       : FillRule::FILL_EVEN_ODD;
 | |
|   RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder(fillRule);
 | |
| 
 | |
|   nsTArray<nsPoint> vertices =
 | |
|       ShapeUtils::ComputePolygonVertices(basicShape, aRefBox);
 | |
|   if (vertices.IsEmpty()) {
 | |
|     MOZ_ASSERT_UNREACHABLE(
 | |
|         "ComputePolygonVertices() should've given us some vertices!");
 | |
|   } else {
 | |
|     nscoord appUnitsPerDevPixel =
 | |
|         mTargetFrame->PresContext()->AppUnitsPerDevPixel();
 | |
|     builder->MoveTo(NSPointToPoint(vertices[0], appUnitsPerDevPixel));
 | |
|     for (size_t i = 1; i < vertices.Length(); ++i) {
 | |
|       builder->LineTo(NSPointToPoint(vertices[i], appUnitsPerDevPixel));
 | |
|     }
 | |
|   }
 | |
|   builder->Close();
 | |
|   return builder->Finish();
 | |
| }
 | |
| 
 | |
| already_AddRefed<Path> CSSClipPathInstance::CreateClipPathInset(
 | |
|     DrawTarget* aDrawTarget, const nsRect& aRefBox) {
 | |
|   const auto& basicShape = *mClipPathStyle.AsShape()._0;
 | |
| 
 | |
|   RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
 | |
| 
 | |
|   nscoord appUnitsPerDevPixel =
 | |
|       mTargetFrame->PresContext()->AppUnitsPerDevPixel();
 | |
| 
 | |
|   nsRect insetRect = ShapeUtils::ComputeInsetRect(basicShape, aRefBox);
 | |
|   const Rect insetRectPixels = NSRectToRect(insetRect, appUnitsPerDevPixel);
 | |
|   nscoord appUnitsRadii[8];
 | |
| 
 | |
|   if (ShapeUtils::ComputeInsetRadii(basicShape, aRefBox, appUnitsRadii)) {
 | |
|     RectCornerRadii corners;
 | |
|     nsCSSRendering::ComputePixelRadii(appUnitsRadii, appUnitsPerDevPixel,
 | |
|                                       &corners);
 | |
| 
 | |
|     AppendRoundedRectToPath(builder, insetRectPixels, corners, true);
 | |
|   } else {
 | |
|     AppendRectToPath(builder, insetRectPixels, true);
 | |
|   }
 | |
|   return builder->Finish();
 | |
| }
 | |
| 
 | |
| already_AddRefed<Path> CSSClipPathInstance::CreateClipPathPath(
 | |
|     DrawTarget* aDrawTarget) {
 | |
|   const auto& path = mClipPathStyle.AsPath();
 | |
| 
 | |
|   RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder(
 | |
|       path.fill == StyleFillRule::Nonzero ? FillRule::FILL_WINDING
 | |
|                                           : FillRule::FILL_EVEN_ODD);
 | |
|   float scale = float(AppUnitsPerCSSPixel()) /
 | |
|                 mTargetFrame->PresContext()->AppUnitsPerDevPixel();
 | |
|   return SVGPathData::BuildPath(path.path._0.AsSpan(), builder,
 | |
|                                 StyleStrokeLinecap::Butt, 0.0, scale);
 | |
| }
 | |
| 
 | |
| }  // namespace mozilla
 |